From fcc0bb81f64fe26df67110d48f8bb6d6335eeccd Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Tue, 18 Mar 2025 10:09:40 +0900 Subject: [PATCH 01/22] =?UTF-8?q?=E2=9C=A8=20Feat=20:=20swagger=20ui=20as?= =?UTF-8?q?=20root?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/README.md | 29 +++++++++++++++++++++++- back/build.gradle.kts | 5 ++-- back/src/main/resources/application.yaml | 4 ++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/back/README.md b/back/README.md index 9fd9e58..bd423e6 100644 --- a/back/README.md +++ b/back/README.md @@ -21,4 +21,31 @@ com.example TODO: refactor project structure -TODO: add questions \ No newline at end of file +TODO: add questions + + +# 2. 표현식 Expression +1. 숫자 연산자 우선순위 오해 OperationPriority + - 이진 시프트 BinaryShift + - 비트 연산자 BitwiseOperator +2. 조건식의 괄호 누락 MissingParentheses + - &&, ||의 우선순위 LogicalOperatorPrecedence + - 조건 연산자와 덧셈 TernaryWithAddition + - 조건 연산자와 null 검사 TernaryWithNullCheck +3. 덧셈이 아닌 결합으로 작동 StringConcatenation +4. 멀티라인 문자열 리터럴 MultilineStringLiteral +5. 단항 덧셈 연산자 UnaryPlusOperator +6. 조건 표현식의 묵시적 타입 변환 ImplicitTypeConversion + - 조건 표현식의 박싱된 숫자 BoxedNumberConditional + - 중첩 조건 표현식 NestedConditional +7. 비단락 논리 연산자 사용 NonShortCircuitOperator +8. &&와 || 혼용 MixedLogicalOperators +9. 잘못된 가변 인수 호출 VarArgsIssues + - 모호한 가변 인수 호출 AmbiguousVarArgs + - 배열과 컬렉션 혼용 ArrayCollectionMixup + - 가변 인수에 원시 배열 전달 PrimitiveArrayToVarArgs +10. 조건 연산자와 가변 인수 호출 TernaryWithVarArgs +11. 반환값 무시 IgnoredReturnValue +12. 새롭게 생성된 객체를 사용하지 않음 UnusedObjects +13. 잘못된 메서드를 참조하는 바인딩 IncorrectMethodBinding +14. 메서드 참조 시 잘못된 메서드 지정 WrongMethodReference \ No newline at end of file diff --git a/back/build.gradle.kts b/back/build.gradle.kts index 82a5986..692064e 100644 --- a/back/build.gradle.kts +++ b/back/build.gradle.kts @@ -41,9 +41,8 @@ dependencies { testCompileOnly("org.projectlombok:lombok") testAnnotationProcessor("org.projectlombok:lombok") - // Document RESTful services by combining hand-written with Asciidoctor - // and auto-generated snippets produced with Spring MVC Test - testImplementation("org.springframework.restdocs:spring-restdocs-mockmvc") + // SpringDoc OpenAPI UI for REST API documentation + implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.5") // Spring Data base dependency without any database-specific functionality implementation("org.springframework.data:spring-data-commons") diff --git a/back/src/main/resources/application.yaml b/back/src/main/resources/application.yaml index b1084f1..2cb627f 100644 --- a/back/src/main/resources/application.yaml +++ b/back/src/main/resources/application.yaml @@ -1,3 +1,7 @@ spring: application: name: mistakes + +springdoc: + swagger-ui: + use-root-path: true From 9f89c588a456a05fbbe4c313a0800bbcbaed745b Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Tue, 18 Mar 2025 16:33:40 +0900 Subject: [PATCH 02/22] =?UTF-8?q?=F0=9F=94=84=20Refactor=20:=20visibility?= =?UTF-8?q?=20convention?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 1 + .../example/mistakes/api/BaseAPITests.java | 36 +++---------------- .../mistakes/api/hello/HelloAPITests.java | 4 +-- .../api/questions/QuestionAPITests.java | 4 +-- 4 files changed, 9 insertions(+), 36 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 0958725..a12fb04 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,6 +8,7 @@ }, "java.completion.favoriteStaticMembers": [ "org.hamcrest.Matchers.*", + "org.junit.jupiter.api.*", "org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*", "org.springframework.test.web.servlet.result.MockMvcResultMatchers.*" ] diff --git a/back/src/test/java/com/example/mistakes/api/BaseAPITests.java b/back/src/test/java/com/example/mistakes/api/BaseAPITests.java index 815178d..3b92bb6 100644 --- a/back/src/test/java/com/example/mistakes/api/BaseAPITests.java +++ b/back/src/test/java/com/example/mistakes/api/BaseAPITests.java @@ -12,7 +12,6 @@ import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.DynamicTest; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; @@ -28,12 +27,12 @@ @SpringBootTest @AutoConfigureMockMvc @Slf4j -public class BaseAPITests { +class BaseAPITests { @Autowired private MockMvc mockMvc; @Autowired private RequestMappingHandlerMapping handlerMapping; - private ResultMatcher[] matchers() { + ResultMatcher[] matchers() { return new ResultMatcher[] { status().isOk(), content().contentType(MediaType.APPLICATION_JSON), @@ -42,39 +41,12 @@ private ResultMatcher[] matchers() { }; } - @Test - public void testOneStatic() throws Exception { - mockMvc.perform(get("/api/questions/t1")).andExpectAll(matchers()); - } - - @Test - public void testSomeStatic() throws Exception { - Iterable apis = - List.of( - // "/api/hello", - "/api/questions/t1", "/api/questions/t2"); - for (String api : apis) { - mockMvc.perform(get(api)).andExpectAll(matchers()); - } - } - - @TestFactory - // testSingleDynamic() - public Stream testSomeStaticAsFactory() { - var urls = List.of("/api/questions/t1", "/api/questions/t2"); - return urls.parallelStream() - .map( - url -> - DynamicTest.dynamicTest( - url, () -> mockMvc.perform(get(url)).andExpectAll(matchers()))); - } - /** * Extract `GET /api/**` endpoints from handlerMapping * * @return List of endpoints */ - private List getEndpoints() { + List getEndpoints() { var keys = handlerMapping.getHandlerMethods().keySet().stream().toList(); @@ -105,7 +77,7 @@ private List getEndpoints() { } @TestFactory - public Stream testSomeDynamicAsFactory() { + Stream testSomeDynamicAsFactory() { var urls = List.of("/api/questions/t1", "/api/questions/t2"); var endpoints = getEndpoints(); assert endpoints.containsAll(urls); diff --git a/back/src/test/java/com/example/mistakes/api/hello/HelloAPITests.java b/back/src/test/java/com/example/mistakes/api/hello/HelloAPITests.java index 3f108cc..b6623d5 100644 --- a/back/src/test/java/com/example/mistakes/api/hello/HelloAPITests.java +++ b/back/src/test/java/com/example/mistakes/api/hello/HelloAPITests.java @@ -14,12 +14,12 @@ @SpringBootTest @AutoConfigureMockMvc -public class HelloAPITests { +class HelloAPITests { @Autowired private MockMvc mockMvc; @Test - public void testHelloEndpoint() throws Exception { + void testHelloEndpoint() throws Exception { mockMvc .perform(get("/api/hello")) .andExpect(status().isOk()) diff --git a/back/src/test/java/com/example/mistakes/api/questions/QuestionAPITests.java b/back/src/test/java/com/example/mistakes/api/questions/QuestionAPITests.java index 84de3eb..dd01830 100644 --- a/back/src/test/java/com/example/mistakes/api/questions/QuestionAPITests.java +++ b/back/src/test/java/com/example/mistakes/api/questions/QuestionAPITests.java @@ -14,11 +14,11 @@ @SpringBootTest @AutoConfigureMockMvc -public class QuestionAPITests { +class QuestionAPITests { @Autowired private MockMvc mockMvc; - public void testGetQuestion() throws Exception { + void testGetQuestion() throws Exception { mockMvc .perform(get("/api/questions/t1").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) From 57a65cbe013e6ecd2da7460e0739088b818d6582 Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Tue, 18 Mar 2025 18:21:58 +0900 Subject: [PATCH 03/22] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20Chore=20:=20auto?= =?UTF-8?q?=20hot=20reload?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a12fb04..b26eb03 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { + // https://code.visualstudio.com/docs/java/java-debugging "java.configuration.updateBuildConfiguration": "automatic", "java.compile.nullAnalysis.mode": "automatic", "java.dependency.syncWithFolderExplorer": true, @@ -11,5 +12,6 @@ "org.junit.jupiter.api.*", "org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*", "org.springframework.test.web.servlet.result.MockMvcResultMatchers.*" - ] + ], + "java.debug.settings.hotCodeReplace": "manual" // change to "auto" if you want to enable } From 69883173371116e873eaed2d0c5cd8b042ecf51a Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Tue, 18 Mar 2025 18:34:09 +0900 Subject: [PATCH 04/22] =?UTF-8?q?=E2=9C=A8=20Feat=20:=20assign=20example?= =?UTF-8?q?=2001?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/questions/QuestionController.java | 8 +- .../api/questions/QuestionEntity.java | 14 ++- .../questions/service/QuestionService.java | 8 +- .../service/QuestionServiceImpl.java | 25 +++-- .../service/ReadOnlyFsRepository.java | 91 ++++++++++--------- .../mistakes/base/type/definition/ID.java | 5 + .../expression/_01_OperationPriority.java | 32 +++++++ .../api/questions/QuestionServiceTests.java | 9 ++ .../_01_OperationPriorityTests.java | 49 ++++++++++ 9 files changed, 182 insertions(+), 59 deletions(-) create mode 100644 back/src/main/java/com/example/mistakes/base/type/definition/ID.java create mode 100644 back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java create mode 100644 back/src/test/java/com/example/mistakes/api/questions/QuestionServiceTests.java create mode 100644 back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java diff --git a/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java b/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java index 4c41563..ca202ab 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java +++ b/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java @@ -15,10 +15,10 @@ public class QuestionController { private final List data = // TODO: get data from service List.of( - new QuestionEntity("A"), - new QuestionEntity("B"), - new QuestionEntity("C"), - new QuestionEntity("D")); + new QuestionEntity("A", "A", "A"), + new QuestionEntity("B", "B", "B"), + new QuestionEntity("C", "C", "C"), + new QuestionEntity("D", "D", "D")); @GetMapping("/t1") public ResponseEntity> t1() { diff --git a/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java b/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java index d01fd03..53b122a 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java +++ b/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java @@ -1,5 +1,17 @@ package com.example.mistakes.api.questions; +import com.example.mistakes.base.type.definition.ID; import com.example.mistakes.base.type.definition.Message; -public record QuestionEntity(String message) implements Message {} +public record QuestionEntity(String message, String sourceBefore, String sourceAfter) + implements Message, ID { + + public Number getId() { + String filtered = message.replaceAll("[^0-9]", ""); + if (filtered.isEmpty()) { + return message.hashCode() - 1; + } + Number id = Integer.parseInt(filtered); + return id; + } +} diff --git a/back/src/main/java/com/example/mistakes/api/questions/service/QuestionService.java b/back/src/main/java/com/example/mistakes/api/questions/service/QuestionService.java index cc0c200..d51e743 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/service/QuestionService.java +++ b/back/src/main/java/com/example/mistakes/api/questions/service/QuestionService.java @@ -1,14 +1,16 @@ package com.example.mistakes.api.questions.service; -interface QuestionService { +public interface QuestionService { + + void add(T entity); // 1. query by question id - T findById(ID id); + T findById(Integer id); // 2. chapter Iterable findByChapter(String chapter); - Iterable findByChapter(ID id); + Iterable findByChapter(Integer number); // 3. keywords enum ConditionType { diff --git a/back/src/main/java/com/example/mistakes/api/questions/service/QuestionServiceImpl.java b/back/src/main/java/com/example/mistakes/api/questions/service/QuestionServiceImpl.java index 0729598..a060a1c 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/service/QuestionServiceImpl.java +++ b/back/src/main/java/com/example/mistakes/api/questions/service/QuestionServiceImpl.java @@ -7,32 +7,41 @@ @Service @RequiredArgsConstructor -public class QuestionServiceImpl implements QuestionService { +class QuestionServiceImpl implements QuestionService { - @Autowired private final ReadOnlyFsRepository repository; + @Autowired private final ReadOnlyFsRepository repository; @Override - public QuestionEntity findById(Long id) { + public void add(QuestionEntity entity) { + repository.save(entity); + } + + @Override + public QuestionEntity findById(Integer id) { return repository.findById(id).orElse(null); } @Override public Iterable findByChapter(String chapter) { - throw new UnsupportedOperationException("Not implemented"); + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'findByChapter'"); } @Override - public Iterable findByChapter(Long id) { - throw new UnsupportedOperationException("Not implemented"); + public Iterable findByChapter(Integer number) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'findByChapter'"); } @Override public Iterable findByKeywords(String keyword) { - throw new UnsupportedOperationException("Not implemented"); + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'findByKeywords'"); } @Override public Iterable findByKeywords(Iterable keywords, ConditionType type) { - throw new UnsupportedOperationException("Not implemented"); + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'findByKeywords'"); } } diff --git a/back/src/main/java/com/example/mistakes/api/questions/service/ReadOnlyFsRepository.java b/back/src/main/java/com/example/mistakes/api/questions/service/ReadOnlyFsRepository.java index 333246b..dae7d49 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/service/ReadOnlyFsRepository.java +++ b/back/src/main/java/com/example/mistakes/api/questions/service/ReadOnlyFsRepository.java @@ -1,5 +1,8 @@ package com.example.mistakes.api.questions.service; +import com.example.mistakes.base.type.definition.ID; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; import java.util.function.Function; import org.springframework.data.domain.Example; @@ -9,6 +12,7 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; import org.springframework.data.repository.query.QueryByExampleExecutor; +import org.springframework.lang.NonNull; import org.springframework.stereotype.Repository; /** @@ -17,124 +21,125 @@ *

Repository interface for read-only operations * * @param Entity - * @param Entity ID type * @see org.springframework.data.repository.Repository */ @Repository -class ReadOnlyFsRepository implements CrudRepository, QueryByExampleExecutor { +class ReadOnlyFsRepository + implements CrudRepository, QueryByExampleExecutor { + + Map data = new HashMap<>(); @Override - public long count() { - // TODO Auto-generated method stub - return 0; + public @NonNull S save(@NonNull S entity) { + data.put(entity.getId(), entity); + return entity; } @Override - public void delete(T entity) { - // TODO Auto-generated method stub - + public @NonNull Optional findById(@NonNull Integer id) { + return Optional.ofNullable(data.get(id)); } @Override - public void deleteAll() { + public Optional findOne(Example example) { // TODO Auto-generated method stub - + throw new UnsupportedOperationException("Unimplemented method 'findOne'"); } @Override - public void deleteAll(Iterable entities) { + public Iterable findAll(Example example) { // TODO Auto-generated method stub - + throw new UnsupportedOperationException("Unimplemented method 'findAll'"); } @Override - public void deleteAllById(Iterable ids) { + public Iterable findAll(Example example, Sort sort) { // TODO Auto-generated method stub - + throw new UnsupportedOperationException("Unimplemented method 'findAll'"); } @Override - public void deleteById(ID id) { + public Page findAll(Example example, Pageable pageable) { // TODO Auto-generated method stub - + throw new UnsupportedOperationException("Unimplemented method 'findAll'"); } @Override - public boolean existsById(ID id) { + public long count(Example example) { // TODO Auto-generated method stub - return false; + throw new UnsupportedOperationException("Unimplemented method 'count'"); } @Override - public Iterable findAll() { + public boolean exists(Example example) { // TODO Auto-generated method stub - return null; + throw new UnsupportedOperationException("Unimplemented method 'exists'"); } @Override - public Iterable findAllById(Iterable ids) { + public R findBy( + Example example, Function, R> queryFunction) { // TODO Auto-generated method stub - return null; + throw new UnsupportedOperationException("Unimplemented method 'findBy'"); } @Override - public Optional findById(ID id) { + public Iterable saveAll(Iterable entities) { // TODO Auto-generated method stub - return Optional.empty(); + throw new UnsupportedOperationException("Unimplemented method 'saveAll'"); } @Override - public S save(S entity) { + public boolean existsById(Integer id) { // TODO Auto-generated method stub - return null; + throw new UnsupportedOperationException("Unimplemented method 'existsById'"); } @Override - public Iterable saveAll(Iterable entities) { + public Iterable findAll() { // TODO Auto-generated method stub - return null; + throw new UnsupportedOperationException("Unimplemented method 'findAll'"); } @Override - public long count(Example example) { + public Iterable findAllById(Iterable ids) { // TODO Auto-generated method stub - return 0; + throw new UnsupportedOperationException("Unimplemented method 'findAllById'"); } @Override - public boolean exists(Example example) { + public long count() { // TODO Auto-generated method stub - return false; + throw new UnsupportedOperationException("Unimplemented method 'count'"); } @Override - public Iterable findAll(Example example) { + public void deleteById(Integer id) { // TODO Auto-generated method stub - return null; + throw new UnsupportedOperationException("Unimplemented method 'deleteById'"); } @Override - public Iterable findAll(Example example, Sort sort) { + public void delete(T entity) { // TODO Auto-generated method stub - return null; + throw new UnsupportedOperationException("Unimplemented method 'delete'"); } @Override - public Page findAll(Example example, Pageable pageable) { + public void deleteAllById(Iterable ids) { // TODO Auto-generated method stub - return null; + throw new UnsupportedOperationException("Unimplemented method 'deleteAllById'"); } @Override - public R findBy( - Example example, Function, R> queryFunction) { + public void deleteAll(Iterable entities) { // TODO Auto-generated method stub - return null; + throw new UnsupportedOperationException("Unimplemented method 'deleteAll'"); } @Override - public Optional findOne(Example example) { + public void deleteAll() { // TODO Auto-generated method stub - return Optional.empty(); + throw new UnsupportedOperationException("Unimplemented method 'deleteAll'"); } } diff --git a/back/src/main/java/com/example/mistakes/base/type/definition/ID.java b/back/src/main/java/com/example/mistakes/base/type/definition/ID.java new file mode 100644 index 0000000..3e8cd30 --- /dev/null +++ b/back/src/main/java/com/example/mistakes/base/type/definition/ID.java @@ -0,0 +1,5 @@ +package com.example.mistakes.base.type.definition; + +public interface ID { + Number getId(); +} diff --git a/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java b/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java new file mode 100644 index 0000000..a68fb39 --- /dev/null +++ b/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java @@ -0,0 +1,32 @@ +package com.example.mistakes.expression; + +import com.example.mistakes.api.questions.QuestionEntity; +import com.example.mistakes.api.questions.service.QuestionService; +import java.util.List; +import org.springframework.stereotype.Component; + +@Component +public class _01_OperationPriority { + + List sourceBefore = + List.of("int before (short lo, short hi) {" + " return lo << 16 + hi;" + "}"); + List sourceAfter = + List.of("int after (short lo, short hi) {" + " return (lo << 16) + hi;" + "}"); + QuestionEntity entity = + new QuestionEntity( + this.getClass().getSimpleName(), + String.join("\n", sourceBefore), + String.join("\n", sourceAfter)); + + public _01_OperationPriority(QuestionService service) { + service.add(entity); + } + + int before(short lo, short hi) { + return lo << 16 + hi; + } + + int after(short lo, short hi) { + return (lo << 16) + hi; + } +} diff --git a/back/src/test/java/com/example/mistakes/api/questions/QuestionServiceTests.java b/back/src/test/java/com/example/mistakes/api/questions/QuestionServiceTests.java new file mode 100644 index 0000000..073297d --- /dev/null +++ b/back/src/test/java/com/example/mistakes/api/questions/QuestionServiceTests.java @@ -0,0 +1,9 @@ +package com.example.mistakes.api.questions; + +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class QuestionAPITests { + + void testGetQuestion() throws Exception {} +} diff --git a/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java b/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java new file mode 100644 index 0000000..f8668b1 --- /dev/null +++ b/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java @@ -0,0 +1,49 @@ +package com.example.mistakes.expression; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.example.mistakes.api.questions.QuestionEntity; +import com.example.mistakes.api.questions.service.QuestionService; +import java.util.Random; +import java.util.stream.Stream; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +@RequiredArgsConstructor +class _01_OperationPriorityTests { + + @Autowired private QuestionService service; + @Autowired private _01_OperationPriority target; + + @Test + void testRegistration() { + assertEquals(target.entity, service.findById(1)); + } + + @ParameterizedTest + @MethodSource("randomArguments") + @DisplayName("Fuzz test with random inputs using ParameterizedTest") + void fuzzTest(short lo, short hi) { + assertEquals( + target.after(lo, hi), + lo * Math.pow(2, 16) + hi, + "Fuzz test failed with lo=%d hi=%d".formatted(lo, hi)); + } + + static Stream randomArguments() { + Random random = new Random(); + return Stream.generate( + () -> + Arguments.of( + (short) random.nextInt(Short.MAX_VALUE), + (short) random.nextInt(Short.MAX_VALUE))) + .limit(100); + } +} From d1771f7398abf76857f472d0ff8195c5e9b96b1c Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Tue, 18 Mar 2025 21:45:36 +0900 Subject: [PATCH 05/22] =?UTF-8?q?=F0=9F=9A=A7=20WIP=20:=20need=20impl=20fs?= =?UTF-8?q?=20read?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mistakes/QuestionEntityBuilder.java | 11 ++++ .../example/mistakes/api/hello/HelloDTO.java | 2 +- .../api/questions/QuestionController.java | 28 +++++----- .../api/questions/QuestionEntity.java | 15 ++++-- .../questions/service/QuestionService.java | 24 --------- .../{type => }/template/ResponseManyDTO.java | 7 +-- .../example/mistakes/base/type/FsMeta.java | 7 +++ .../com/example/mistakes/base/type/ID.java | 5 ++ .../example/mistakes/base/type/Message.java | 5 ++ .../example/mistakes/base/type/Response.java | 5 ++ .../type/{definition => }/ResponseMany.java | 2 +- .../mistakes/base/type/definition/ID.java | 5 -- .../base/type/definition/Message.java | 5 -- .../base/type/definition/Response.java | 5 -- .../expression/_01_OperationPriority.java | 20 +++---- .../mistakes/service/QuestionService.java | 28 ++++++++++ .../service/QuestionServiceImpl.java | 20 ++++--- .../service/ReadOnlyFsRepository.java | 30 +++++------ .../_01_OperationPriorityTests.java | 52 ++++++++++++++++++- 19 files changed, 179 insertions(+), 97 deletions(-) create mode 100644 back/src/main/java/com/example/mistakes/QuestionEntityBuilder.java delete mode 100644 back/src/main/java/com/example/mistakes/api/questions/service/QuestionService.java rename back/src/main/java/com/example/mistakes/base/{type => }/template/ResponseManyDTO.java (77%) create mode 100644 back/src/main/java/com/example/mistakes/base/type/FsMeta.java create mode 100644 back/src/main/java/com/example/mistakes/base/type/ID.java create mode 100644 back/src/main/java/com/example/mistakes/base/type/Message.java create mode 100644 back/src/main/java/com/example/mistakes/base/type/Response.java rename back/src/main/java/com/example/mistakes/base/type/{definition => }/ResponseMany.java (61%) delete mode 100644 back/src/main/java/com/example/mistakes/base/type/definition/ID.java delete mode 100644 back/src/main/java/com/example/mistakes/base/type/definition/Message.java delete mode 100644 back/src/main/java/com/example/mistakes/base/type/definition/Response.java create mode 100644 back/src/main/java/com/example/mistakes/service/QuestionService.java rename back/src/main/java/com/example/mistakes/{api/questions => }/service/QuestionServiceImpl.java (64%) rename back/src/main/java/com/example/mistakes/{api/questions => }/service/ReadOnlyFsRepository.java (86%) diff --git a/back/src/main/java/com/example/mistakes/QuestionEntityBuilder.java b/back/src/main/java/com/example/mistakes/QuestionEntityBuilder.java new file mode 100644 index 0000000..5659863 --- /dev/null +++ b/back/src/main/java/com/example/mistakes/QuestionEntityBuilder.java @@ -0,0 +1,11 @@ +package com.example.mistakes; + +import com.example.mistakes.api.questions.QuestionEntity; + +public class QuestionEntityBuilder { + public QuestionEntity build(Class cls) { + String pkgName = cls.getPackageName(); + String clsName = cls.getSimpleName(); + return new QuestionEntity("%s/%s.java".formatted(pkgName, clsName)); + } +} diff --git a/back/src/main/java/com/example/mistakes/api/hello/HelloDTO.java b/back/src/main/java/com/example/mistakes/api/hello/HelloDTO.java index 4f5cada..13cbccd 100644 --- a/back/src/main/java/com/example/mistakes/api/hello/HelloDTO.java +++ b/back/src/main/java/com/example/mistakes/api/hello/HelloDTO.java @@ -1,6 +1,6 @@ package com.example.mistakes.api.hello; -import com.example.mistakes.base.type.definition.Message; +import com.example.mistakes.base.type.Message; @Deprecated public record HelloDTO(String message) implements Message {} diff --git a/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java b/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java index ca202ab..7a51c5c 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java +++ b/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java @@ -1,8 +1,12 @@ package com.example.mistakes.api.questions; -import com.example.mistakes.base.type.definition.ResponseMany; -import com.example.mistakes.base.type.template.ResponseManyDTO; + +import com.example.mistakes.base.template.ResponseManyDTO; +import com.example.mistakes.base.type.ResponseMany; +import com.example.mistakes.service.QuestionService; + import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -11,30 +15,30 @@ @RestController @RequestMapping("/api/questions") public class QuestionController { + @Autowired private final QuestionService service; - private final List data = - // TODO: get data from service - List.of( - new QuestionEntity("A", "A", "A"), - new QuestionEntity("B", "B", "B"), - new QuestionEntity("C", "C", "C"), - new QuestionEntity("D", "D", "D")); + public QuestionController(QuestionService service) { + this.service = service; + } @GetMapping("/t1") public ResponseEntity> t1() { - final var dto = new ResRecordQuestion(this.data, this.data.size()); + final var data = this.service.findAll(); + final var dto = new ResRecordQuestion(data, data.size()); return ResponseEntity.ok().body(dto); } @GetMapping("/t2") public ResponseEntity> t2() { - final var dto = new ResClassQuestion(this.data); + final var data = this.service.findAll(); + final var dto = new ResClassQuestion(data); return ResponseEntity.ok().body(dto); } @GetMapping("/t3") public ResponseEntity> t3() { - final var dto = ResClassOfQuestion.of(this.data); + final var data = this.service.findAll(); + final var dto = ResClassOfQuestion.of(data); return ResponseEntity.ok().body(dto); } } diff --git a/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java b/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java index 53b122a..80d2496 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java +++ b/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java @@ -1,10 +1,12 @@ package com.example.mistakes.api.questions; -import com.example.mistakes.base.type.definition.ID; -import com.example.mistakes.base.type.definition.Message; +import com.example.mistakes.base.type.FsMeta; +import com.example.mistakes.base.type.ID; +import com.example.mistakes.base.type.Message; +import java.nio.file.Path; +import java.nio.file.Paths; -public record QuestionEntity(String message, String sourceBefore, String sourceAfter) - implements Message, ID { +public record QuestionEntity(String message) implements Message, ID, FsMeta { public Number getId() { String filtered = message.replaceAll("[^0-9]", ""); @@ -14,4 +16,9 @@ public Number getId() { Number id = Integer.parseInt(filtered); return id; } + + public Path getPath() { + String prefix = "src/main/java"; + return Paths.get(prefix, message); + } } diff --git a/back/src/main/java/com/example/mistakes/api/questions/service/QuestionService.java b/back/src/main/java/com/example/mistakes/api/questions/service/QuestionService.java deleted file mode 100644 index d51e743..0000000 --- a/back/src/main/java/com/example/mistakes/api/questions/service/QuestionService.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.example.mistakes.api.questions.service; - -public interface QuestionService { - - void add(T entity); - - // 1. query by question id - T findById(Integer id); - - // 2. chapter - Iterable findByChapter(String chapter); - - Iterable findByChapter(Integer number); - - // 3. keywords - enum ConditionType { - AND, - OR, - } - - Iterable findByKeywords(String keyword); - - Iterable findByKeywords(Iterable keywords, ConditionType type); -} diff --git a/back/src/main/java/com/example/mistakes/base/type/template/ResponseManyDTO.java b/back/src/main/java/com/example/mistakes/base/template/ResponseManyDTO.java similarity index 77% rename from back/src/main/java/com/example/mistakes/base/type/template/ResponseManyDTO.java rename to back/src/main/java/com/example/mistakes/base/template/ResponseManyDTO.java index 391f8cc..4796e68 100644 --- a/back/src/main/java/com/example/mistakes/base/type/template/ResponseManyDTO.java +++ b/back/src/main/java/com/example/mistakes/base/template/ResponseManyDTO.java @@ -1,11 +1,12 @@ -package com.example.mistakes.base.type.template; +package com.example.mistakes.base.template; -import com.example.mistakes.base.type.definition.ResponseMany; +import com.example.mistakes.base.type.ResponseMany; import java.util.List; import lombok.Getter; /** - * Wrapper class for implementing `ResponseMany` interface while supporting compatability with record-based DTOs + * Wrapper class for implementing `ResponseMany` interface while supporting compatability with + * record-based DTOs * * @param type of result * @see ResponseMany diff --git a/back/src/main/java/com/example/mistakes/base/type/FsMeta.java b/back/src/main/java/com/example/mistakes/base/type/FsMeta.java new file mode 100644 index 0000000..cb98c09 --- /dev/null +++ b/back/src/main/java/com/example/mistakes/base/type/FsMeta.java @@ -0,0 +1,7 @@ +package com.example.mistakes.base.type; + +import java.nio.file.Path; + +public interface FsMeta { + Path getPath(); +} diff --git a/back/src/main/java/com/example/mistakes/base/type/ID.java b/back/src/main/java/com/example/mistakes/base/type/ID.java new file mode 100644 index 0000000..ea4402f --- /dev/null +++ b/back/src/main/java/com/example/mistakes/base/type/ID.java @@ -0,0 +1,5 @@ +package com.example.mistakes.base.type; + +public interface ID { + Number getId(); +} diff --git a/back/src/main/java/com/example/mistakes/base/type/Message.java b/back/src/main/java/com/example/mistakes/base/type/Message.java new file mode 100644 index 0000000..d07d62b --- /dev/null +++ b/back/src/main/java/com/example/mistakes/base/type/Message.java @@ -0,0 +1,5 @@ +package com.example.mistakes.base.type; + +public interface Message { + String message(); +} diff --git a/back/src/main/java/com/example/mistakes/base/type/Response.java b/back/src/main/java/com/example/mistakes/base/type/Response.java new file mode 100644 index 0000000..1a29588 --- /dev/null +++ b/back/src/main/java/com/example/mistakes/base/type/Response.java @@ -0,0 +1,5 @@ +package com.example.mistakes.base.type; + +public interface Response { + T result(); +} diff --git a/back/src/main/java/com/example/mistakes/base/type/definition/ResponseMany.java b/back/src/main/java/com/example/mistakes/base/type/ResponseMany.java similarity index 61% rename from back/src/main/java/com/example/mistakes/base/type/definition/ResponseMany.java rename to back/src/main/java/com/example/mistakes/base/type/ResponseMany.java index b45bb9e..b37ec74 100644 --- a/back/src/main/java/com/example/mistakes/base/type/definition/ResponseMany.java +++ b/back/src/main/java/com/example/mistakes/base/type/ResponseMany.java @@ -1,4 +1,4 @@ -package com.example.mistakes.base.type.definition; +package com.example.mistakes.base.type; public interface ResponseMany { Iterable result(); diff --git a/back/src/main/java/com/example/mistakes/base/type/definition/ID.java b/back/src/main/java/com/example/mistakes/base/type/definition/ID.java deleted file mode 100644 index 3e8cd30..0000000 --- a/back/src/main/java/com/example/mistakes/base/type/definition/ID.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.example.mistakes.base.type.definition; - -public interface ID { - Number getId(); -} diff --git a/back/src/main/java/com/example/mistakes/base/type/definition/Message.java b/back/src/main/java/com/example/mistakes/base/type/definition/Message.java deleted file mode 100644 index 0facc04..0000000 --- a/back/src/main/java/com/example/mistakes/base/type/definition/Message.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.example.mistakes.base.type.definition; - -public interface Message { - String message(); -} diff --git a/back/src/main/java/com/example/mistakes/base/type/definition/Response.java b/back/src/main/java/com/example/mistakes/base/type/definition/Response.java deleted file mode 100644 index 7acf6c5..0000000 --- a/back/src/main/java/com/example/mistakes/base/type/definition/Response.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.example.mistakes.base.type.definition; - -public interface Response { - T result(); -} diff --git a/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java b/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java index a68fb39..bfa47cb 100644 --- a/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java +++ b/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java @@ -1,25 +1,17 @@ package com.example.mistakes.expression; +import com.example.mistakes.QuestionEntityBuilder; import com.example.mistakes.api.questions.QuestionEntity; -import com.example.mistakes.api.questions.service.QuestionService; -import java.util.List; +import com.example.mistakes.service.QuestionService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class _01_OperationPriority { - List sourceBefore = - List.of("int before (short lo, short hi) {" + " return lo << 16 + hi;" + "}"); - List sourceAfter = - List.of("int after (short lo, short hi) {" + " return (lo << 16) + hi;" + "}"); - QuestionEntity entity = - new QuestionEntity( - this.getClass().getSimpleName(), - String.join("\n", sourceBefore), - String.join("\n", sourceAfter)); - - public _01_OperationPriority(QuestionService service) { - service.add(entity); + _01_OperationPriority(@Autowired QuestionService service) { + var builder = new QuestionEntityBuilder<_01_OperationPriority>(); + service.add(builder.build(_01_OperationPriority.class)); } int before(short lo, short hi) { diff --git a/back/src/main/java/com/example/mistakes/service/QuestionService.java b/back/src/main/java/com/example/mistakes/service/QuestionService.java new file mode 100644 index 0000000..c76dab1 --- /dev/null +++ b/back/src/main/java/com/example/mistakes/service/QuestionService.java @@ -0,0 +1,28 @@ +package com.example.mistakes.service; + +import java.util.List; + +public interface QuestionService { + + void add(T entity); + + List findAll(); + + // 1. query by question id + T findById(Integer id); + + // 2. chapter + List findByChapter(String chapter); + + List findByChapter(Integer number); + + // 3. keywords + enum ConditionType { + AND, + OR, + } + + List findByKeywords(String keyword); + + List findByKeywords(Iterable keywords, ConditionType type); +} diff --git a/back/src/main/java/com/example/mistakes/api/questions/service/QuestionServiceImpl.java b/back/src/main/java/com/example/mistakes/service/QuestionServiceImpl.java similarity index 64% rename from back/src/main/java/com/example/mistakes/api/questions/service/QuestionServiceImpl.java rename to back/src/main/java/com/example/mistakes/service/QuestionServiceImpl.java index a060a1c..481500f 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/service/QuestionServiceImpl.java +++ b/back/src/main/java/com/example/mistakes/service/QuestionServiceImpl.java @@ -1,13 +1,14 @@ -package com.example.mistakes.api.questions.service; +package com.example.mistakes.service; import com.example.mistakes.api.questions.QuestionEntity; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor -class QuestionServiceImpl implements QuestionService { +public class QuestionServiceImpl implements QuestionService { @Autowired private final ReadOnlyFsRepository repository; @@ -22,25 +23,32 @@ public QuestionEntity findById(Integer id) { } @Override - public Iterable findByChapter(String chapter) { + public List findAll() { + List res = new java.util.ArrayList(); + repository.findAll().forEach(res::add); + return res; + } + + @Override + public List findByChapter(String chapter) { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'findByChapter'"); } @Override - public Iterable findByChapter(Integer number) { + public List findByChapter(Integer number) { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'findByChapter'"); } @Override - public Iterable findByKeywords(String keyword) { + public List findByKeywords(String keyword) { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'findByKeywords'"); } @Override - public Iterable findByKeywords(Iterable keywords, ConditionType type) { + public List findByKeywords(Iterable keywords, ConditionType type) { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'findByKeywords'"); } diff --git a/back/src/main/java/com/example/mistakes/api/questions/service/ReadOnlyFsRepository.java b/back/src/main/java/com/example/mistakes/service/ReadOnlyFsRepository.java similarity index 86% rename from back/src/main/java/com/example/mistakes/api/questions/service/ReadOnlyFsRepository.java rename to back/src/main/java/com/example/mistakes/service/ReadOnlyFsRepository.java index dae7d49..3bb0e5c 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/service/ReadOnlyFsRepository.java +++ b/back/src/main/java/com/example/mistakes/service/ReadOnlyFsRepository.java @@ -1,6 +1,7 @@ -package com.example.mistakes.api.questions.service; +package com.example.mistakes.service; -import com.example.mistakes.base.type.definition.ID; +import com.example.mistakes.base.type.FsMeta; +import com.example.mistakes.base.type.ID; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -24,8 +25,8 @@ * @see org.springframework.data.repository.Repository */ @Repository -class ReadOnlyFsRepository - implements CrudRepository, QueryByExampleExecutor { +class ReadOnlyFsRepository + implements CrudRepository, QueryByExampleExecutor { Map data = new HashMap<>(); @@ -36,10 +37,15 @@ class ReadOnlyFsRepository } @Override - public @NonNull Optional findById(@NonNull Integer id) { + public @NonNull Optional findById(@NonNull Number id) { return Optional.ofNullable(data.get(id)); } + @Override + public @NonNull Iterable findAll() { + return data.values(); + } + @Override public Optional findOne(Example example) { // TODO Auto-generated method stub @@ -90,19 +96,13 @@ public Iterable saveAll(Iterable entities) { } @Override - public boolean existsById(Integer id) { + public boolean existsById(Number id) { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'existsById'"); } @Override - public Iterable findAll() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'findAll'"); - } - - @Override - public Iterable findAllById(Iterable ids) { + public Iterable findAllById(Iterable ids) { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'findAllById'"); } @@ -114,7 +114,7 @@ public long count() { } @Override - public void deleteById(Integer id) { + public void deleteById(Number id) { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'deleteById'"); } @@ -126,7 +126,7 @@ public void delete(T entity) { } @Override - public void deleteAllById(Iterable ids) { + public void deleteAllById(Iterable ids) { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'deleteAllById'"); } diff --git a/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java b/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java index f8668b1..d1fb141 100644 --- a/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java +++ b/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java @@ -3,7 +3,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import com.example.mistakes.api.questions.QuestionEntity; -import com.example.mistakes.api.questions.service.QuestionService; +import com.example.mistakes.service.QuestionService; +import java.io.IOException; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Random; import java.util.stream.Stream; import lombok.RequiredArgsConstructor; @@ -24,7 +28,51 @@ class _01_OperationPriorityTests { @Test void testRegistration() { - assertEquals(target.entity, service.findById(1)); + System.out.println(service.findAll()); + // assertEquals(target.entity, service.findById(1)); + for (Method method : target.getClass().getDeclaredMethods()) { + System.out.println("Method Name: " + method.getName()); + System.out.println("Method Signature: " + method.toGenericString()); + + try { + // Get the code of the method by reading the source file + Class clazz = target.getClass(); + String className = clazz.getSimpleName(); + String methodName = method.getName(); + + // Construct the path to the source file + String sourceFilePath = + "src/main/java/com/example/mistakes/expression/" + + className + + ".java"; // Adjust path if needed + Path path = Path.of(sourceFilePath); + + // Read all lines from the source file + String methodCode = readMethodCode(path, methodName); + System.out.println("Method Code:\n" + methodCode); + + } catch (Exception e) { + System.err.println( + "Error reading code for method " + method.getName() + ": " + e.getMessage()); + } + } + } + + private String readMethodCode(Path path, String methodName) throws IOException { + StringBuilder methodCode = new StringBuilder(); + boolean methodFound = false; + for (String line : Files.readAllLines(path)) { + if (line.contains(" " + methodName + "(")) { // Simple check for method signature + methodFound = true; + methodCode.append(line).append("\n"); + } else if (methodFound && line.contains("}")) { + methodCode.append(line).append("\n"); + break; // Stop reading after the closing brace of the method + } else if (methodFound) { + methodCode.append(line).append("\n"); + } + } + return methodCode.toString(); } @ParameterizedTest From f78d6861ded85ba33b6a8837cafa4e1ed4e02705 Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Wed, 19 Mar 2025 00:47:15 +0900 Subject: [PATCH 06/22] =?UTF-8?q?=E2=9C=A8=20Feat=20:=20code=20registratio?= =?UTF-8?q?n=20+=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mistakes/QuestionEntityBuilder.java | 2 +- .../api/questions/QuestionEntity.java | 37 ++++++++++++ .../_01_OperationPriorityTests.java | 60 +++++-------------- 3 files changed, 52 insertions(+), 47 deletions(-) diff --git a/back/src/main/java/com/example/mistakes/QuestionEntityBuilder.java b/back/src/main/java/com/example/mistakes/QuestionEntityBuilder.java index 5659863..32b0fb2 100644 --- a/back/src/main/java/com/example/mistakes/QuestionEntityBuilder.java +++ b/back/src/main/java/com/example/mistakes/QuestionEntityBuilder.java @@ -4,7 +4,7 @@ public class QuestionEntityBuilder { public QuestionEntity build(Class cls) { - String pkgName = cls.getPackageName(); + String pkgName = cls.getPackageName().replace(".", "/"); String clsName = cls.getSimpleName(); return new QuestionEntity("%s/%s.java".formatted(pkgName, clsName)); } diff --git a/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java b/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java index 80d2496..7c4541b 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java +++ b/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java @@ -3,6 +3,8 @@ import com.example.mistakes.base.type.FsMeta; import com.example.mistakes.base.type.ID; import com.example.mistakes.base.type.Message; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -21,4 +23,39 @@ public Path getPath() { String prefix = "src/main/java"; return Paths.get(prefix, message); } + + public String getBefore() { + return Code.readMethodCode(getPath(), "before"); + } + + public String getAfter() { + return Code.readMethodCode(getPath(), "after"); + } + + private class Code { + static String readMethodCode(Path path, String methodName) { + try { + return read(path, methodName); + } catch (Exception e) { + return methodName; + } + } + + private static String read(Path path, String methodName) throws IOException { + StringBuilder methodCode = new StringBuilder(); + boolean methodFound = false; + for (String line : Files.readAllLines(path)) { + if (line.contains(" " + methodName + "(")) { // Simple check for method signature + methodFound = true; + methodCode.append(line).append("\n"); + } else if (methodFound && line.contains("}")) { + methodCode.append(line).append("\n"); + break; // Stop reading after the closing brace of the method + } else if (methodFound) { + methodCode.append(line).append("\n"); + } + } + return methodCode.toString(); + } + } } diff --git a/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java b/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java index d1fb141..617f31e 100644 --- a/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java +++ b/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java @@ -1,13 +1,10 @@ package com.example.mistakes.expression; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.example.mistakes.api.questions.QuestionEntity; import com.example.mistakes.service.QuestionService; -import java.io.IOException; -import java.lang.reflect.Method; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.Random; import java.util.stream.Stream; import lombok.RequiredArgsConstructor; @@ -28,51 +25,22 @@ class _01_OperationPriorityTests { @Test void testRegistration() { - System.out.println(service.findAll()); - // assertEquals(target.entity, service.findById(1)); - for (Method method : target.getClass().getDeclaredMethods()) { - System.out.println("Method Name: " + method.getName()); - System.out.println("Method Signature: " + method.toGenericString()); + var classPath = "com/example/mistakes/expression/_01_OperationPriority.java"; + var before = "int before(short lo, short hi) { return lo << 16 + hi; }"; + var after = "int after(short lo, short hi) { return (lo << 16) + hi; }"; + var entity = service.findById(1); + assertEquals(entity.message(), classPath); + assertEquals(entity.getId(), 1); + assertEquals(entity.getPath().toString(), "src/main/java/" + classPath); - try { - // Get the code of the method by reading the source file - Class clazz = target.getClass(); - String className = clazz.getSimpleName(); - String methodName = method.getName(); - - // Construct the path to the source file - String sourceFilePath = - "src/main/java/com/example/mistakes/expression/" - + className - + ".java"; // Adjust path if needed - Path path = Path.of(sourceFilePath); - - // Read all lines from the source file - String methodCode = readMethodCode(path, methodName); - System.out.println("Method Code:\n" + methodCode); - - } catch (Exception e) { - System.err.println( - "Error reading code for method " + method.getName() + ": " + e.getMessage()); - } - } + assertTrue(normalize(entity.getBefore()).contains(before)); + assertTrue(normalize(entity.getAfter()).contains(after)); } - private String readMethodCode(Path path, String methodName) throws IOException { - StringBuilder methodCode = new StringBuilder(); - boolean methodFound = false; - for (String line : Files.readAllLines(path)) { - if (line.contains(" " + methodName + "(")) { // Simple check for method signature - methodFound = true; - methodCode.append(line).append("\n"); - } else if (methodFound && line.contains("}")) { - methodCode.append(line).append("\n"); - break; // Stop reading after the closing brace of the method - } else if (methodFound) { - methodCode.append(line).append("\n"); - } - } - return methodCode.toString(); + // need post-processing to remove redundant spaces + // i.g. double-spaces or tab or newline to single space + private String normalize(String code) { + return code.replaceAll("\\s+", " "); } @ParameterizedTest From a03a136cd0ba40976e0f65e551066c35d97d2c39 Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Wed, 19 Mar 2025 01:14:55 +0900 Subject: [PATCH 07/22] =?UTF-8?q?=E2=9C=A8=20Feat=20:=20before=20+=20after?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/components/GlobalStyles.astro | 1 - front/src/layouts/ApiDataLayout.astro | 45 ++++++++++++++++++++++--- front/src/modules/fetchData.ts | 8 ++++- front/src/modules/getEndpoints.ts | 6 +++- 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/front/src/components/GlobalStyles.astro b/front/src/components/GlobalStyles.astro index 184be63..768a9ec 100644 --- a/front/src/components/GlobalStyles.astro +++ b/front/src/components/GlobalStyles.astro @@ -28,7 +28,6 @@ display: flex; flex-direction: column; align-items: center; - width: 80%; } @media (min-width: 300px) { diff --git a/front/src/layouts/ApiDataLayout.astro b/front/src/layouts/ApiDataLayout.astro index b8b882c..534cac8 100644 --- a/front/src/layouts/ApiDataLayout.astro +++ b/front/src/layouts/ApiDataLayout.astro @@ -11,7 +11,7 @@ export interface Props { baseUrl?: string; } -const { +let { apiPath, title = "API Data Display", description = "Data fetched from API endpoint", @@ -22,12 +22,24 @@ const { console.log(apiPath); const { data } = await fetchDataWithRetry(apiPath); -let { message } = data; +// TODO: layout should applied for each result, not only the first one +const { message, before, after, id, path } = data.result![0]; + +// TODO: use search with regex +title = message + ? message + .split("/") + .pop() + ?.replace(".java", "") + .replace(/^_(\d+)_/, "#$1 ") || title + : title; ---

-

{message || title}

+

+ {title} +

@@ -49,12 +61,37 @@ let { message } = data; />
+ +
+

before

+ + +
+ +
+

after

+ + +
diff --git a/front/src/pages/index.astro b/front/src/pages/index.astro index 481bd2a..0deb7c9 100644 --- a/front/src/pages/index.astro +++ b/front/src/pages/index.astro @@ -1,12 +1,12 @@ --- import GlobalStyles from "@/components/GlobalStyles.astro"; -import ApiDataLayout from "@/layouts/ApiDataLayout.astro"; +import HelloLayout from "@/layouts/HelloLayout.astro"; import Layout from "@/layouts/Landing.astro"; --- - Date: Wed, 19 Mar 2025 11:26:44 +0900 Subject: [PATCH 10/22] =?UTF-8?q?=E2=9C=A8=20Feat=20:=20a=20mistake=20can?= =?UTF-8?q?=20handles=20multiple=20examples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mistakes/QuestionEntityBuilder.java | 4 +- .../api/questions/QuestionEntity.java | 101 +++++++++++++++--- .../com/example/mistakes/base/type/ID.java | 5 - .../mistakes/base/type/Identifiable.java | 5 + .../expression/_01_OperationPriority.java | 15 +++ .../mistakes/service/QuestionService.java | 8 +- .../mistakes/service/QuestionServiceImpl.java | 14 ++- .../service/ReadOnlyFsRepository.java | 18 ++-- .../_01_OperationPriorityTests.java | 37 +++++-- 9 files changed, 162 insertions(+), 45 deletions(-) delete mode 100644 back/src/main/java/com/example/mistakes/base/type/ID.java create mode 100644 back/src/main/java/com/example/mistakes/base/type/Identifiable.java diff --git a/back/src/main/java/com/example/mistakes/QuestionEntityBuilder.java b/back/src/main/java/com/example/mistakes/QuestionEntityBuilder.java index 32b0fb2..c3ba248 100644 --- a/back/src/main/java/com/example/mistakes/QuestionEntityBuilder.java +++ b/back/src/main/java/com/example/mistakes/QuestionEntityBuilder.java @@ -4,8 +4,6 @@ public class QuestionEntityBuilder { public QuestionEntity build(Class cls) { - String pkgName = cls.getPackageName().replace(".", "/"); - String clsName = cls.getSimpleName(); - return new QuestionEntity("%s/%s.java".formatted(pkgName, clsName)); + return new QuestionEntity(cls.getCanonicalName()); } } diff --git a/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java b/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java index 7c4541b..e615b54 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java +++ b/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java @@ -1,50 +1,95 @@ package com.example.mistakes.api.questions; import com.example.mistakes.base.type.FsMeta; -import com.example.mistakes.base.type.ID; +import com.example.mistakes.base.type.Identifiable; import com.example.mistakes.base.type.Message; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import lombok.extern.log4j.Log4j2; -public record QuestionEntity(String message) implements Message, ID, FsMeta { +@Log4j2 +public record QuestionEntity(String message) implements Message, Identifiable, FsMeta { - public Number getId() { + public String getId() { + // new: return 1-2 when got _01_OperationPriority + // when getting `com.example.mistakes.expression._01_OperationPriority.Ex2` + // return id = "_01_2" + Pattern pattern = Pattern.compile("_(\\d+)_\\S+(\\d+)"); + Matcher matcher = pattern.matcher(message); + if (matcher.find()) { + String chapter = matcher.group(1); + String index = matcher.group(2); + return "_%s_%s".formatted(chapter, index); + } + + // legacy: return 1 when got `com/example/mistakes/expression/_01_OperationPriority.java` String filtered = message.replaceAll("[^0-9]", ""); if (filtered.isEmpty()) { - return message.hashCode() - 1; + return String.valueOf(message.hashCode()); } - Number id = Integer.parseInt(filtered); - return id; + return String.valueOf(Integer.parseInt(filtered)); } public Path getPath() { - String prefix = "src/main/java"; - return Paths.get(prefix, message); + // return = src/main/java/com/example/mistakes/expression/_01_OperationPriority.java + // when message = com.example.mistakes.expression._01_OperationPriority.Ex2 + + String pathPrefix = "src/main/java"; + String fileExtension = ".java"; + + Pattern pattern = Pattern.compile("(\\S+_\\d+_[^.]+)"); + Matcher matcher = pattern.matcher(message); + + String path; + if (matcher.find()) { + String group = matcher.group(1); + path = group.replace(".", "/") + fileExtension; + } else { + throw new IllegalArgumentException("Matcher not found: message=" + message); + } + return Paths.get(pathPrefix, path); + } + + public String getClassName() { + // return = Ex2 + // when message = com.example.mistakes.expression._01_OperationPriority.Ex2 + + // temporary assume there is only one nested class + return message.substring(message.lastIndexOf(".") + 1); } public String getBefore() { - return Code.readMethodCode(getPath(), "before"); + return Code.readMethodCode(getPath(), getClassName(), "before"); } public String getAfter() { - return Code.readMethodCode(getPath(), "after"); + return Code.readMethodCode(getPath(), getClassName(), "after"); } private class Code { - static String readMethodCode(Path path, String methodName) { + static String readMethodCode(Path filePath, String className, String methodName) { try { - return read(path, methodName); + return read(filePath, className, methodName); } catch (Exception e) { - return methodName; + return "NoSuchFileException path=%s methodName=%s" + .formatted(filePath, className, methodName); } } - private static String read(Path path, String methodName) throws IOException { + private static String read(Path filePath, String className, String methodName) + throws IOException { + + List classCode = findClassCode(filePath, className); + StringBuilder methodCode = new StringBuilder(); boolean methodFound = false; - for (String line : Files.readAllLines(path)) { + for (String line : classCode) { if (line.contains(" " + methodName + "(")) { // Simple check for method signature methodFound = true; methodCode.append(line).append("\n"); @@ -57,5 +102,31 @@ private static String read(Path path, String methodName) throws IOException { } return methodCode.toString(); } + + private static List findClassCode(Path filePath, String className) throws IOException { + List classCode = new ArrayList<>(); + + boolean classFound = false; + int braceCount = 0; + for (String line : Files.readAllLines(filePath)) { + if (line.contains("class " + className)) { // Simple check for class signature + classFound = true; + classCode.add(line); + braceCount++; + } else if (classFound) { + classCode.add(line); + if (line.contains("{")) { + braceCount++; + } + if (line.contains("}")) { + braceCount--; + if (braceCount == 0) { + break; // Stop reading after the closing brace of the class + } + } + } + } + return classCode; + } } } diff --git a/back/src/main/java/com/example/mistakes/base/type/ID.java b/back/src/main/java/com/example/mistakes/base/type/ID.java deleted file mode 100644 index ea4402f..0000000 --- a/back/src/main/java/com/example/mistakes/base/type/ID.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.example.mistakes.base.type; - -public interface ID { - Number getId(); -} diff --git a/back/src/main/java/com/example/mistakes/base/type/Identifiable.java b/back/src/main/java/com/example/mistakes/base/type/Identifiable.java new file mode 100644 index 0000000..b0daa51 --- /dev/null +++ b/back/src/main/java/com/example/mistakes/base/type/Identifiable.java @@ -0,0 +1,5 @@ +package com.example.mistakes.base.type; + +public interface Identifiable { + T getId(); +} diff --git a/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java b/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java index bfa47cb..8c328cf 100644 --- a/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java +++ b/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java @@ -12,6 +12,8 @@ public class _01_OperationPriority { _01_OperationPriority(@Autowired QuestionService service) { var builder = new QuestionEntityBuilder<_01_OperationPriority>(); service.add(builder.build(_01_OperationPriority.class)); + + service.add(new QuestionEntityBuilder().build(Ex2.class)); } int before(short lo, short hi) { @@ -21,4 +23,17 @@ int before(short lo, short hi) { int after(short lo, short hi) { return (lo << 16) + hi; } + + class Ex2 { + String subContext = "Binary Shift"; + short xmin = 1, ymin = 1, xmax = 1, ymax = 1; + + int before() { + return xmin + ymin << 8 + xmax << 16 + ymax << 24; + } + + int after() { + return xmin + (ymin << 8) + (xmax << 16) + (ymax << 24); + } + } } diff --git a/back/src/main/java/com/example/mistakes/service/QuestionService.java b/back/src/main/java/com/example/mistakes/service/QuestionService.java index c76dab1..c164e97 100644 --- a/back/src/main/java/com/example/mistakes/service/QuestionService.java +++ b/back/src/main/java/com/example/mistakes/service/QuestionService.java @@ -8,13 +8,15 @@ public interface QuestionService { List findAll(); + T find(Number chapter, Number index); + // 1. query by question id - T findById(Integer id); + T findById(String id); // 2. chapter - List findByChapter(String chapter); + List findByChapter(String name); - List findByChapter(Integer number); + List findByChapter(Number index); // 3. keywords enum ConditionType { diff --git a/back/src/main/java/com/example/mistakes/service/QuestionServiceImpl.java b/back/src/main/java/com/example/mistakes/service/QuestionServiceImpl.java index 481500f..9cfcc85 100644 --- a/back/src/main/java/com/example/mistakes/service/QuestionServiceImpl.java +++ b/back/src/main/java/com/example/mistakes/service/QuestionServiceImpl.java @@ -10,7 +10,7 @@ @RequiredArgsConstructor public class QuestionServiceImpl implements QuestionService { - @Autowired private final ReadOnlyFsRepository repository; + @Autowired private final ReadOnlyFsRepository repository; @Override public void add(QuestionEntity entity) { @@ -18,7 +18,13 @@ public void add(QuestionEntity entity) { } @Override - public QuestionEntity findById(Integer id) { + public QuestionEntity find(Number chapter, Number index) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'find'"); + } + + @Override + public QuestionEntity findById(String id) { return repository.findById(id).orElse(null); } @@ -30,13 +36,13 @@ public List findAll() { } @Override - public List findByChapter(String chapter) { + public List findByChapter(String name) { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'findByChapter'"); } @Override - public List findByChapter(Integer number) { + public List findByChapter(Number index) { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'findByChapter'"); } diff --git a/back/src/main/java/com/example/mistakes/service/ReadOnlyFsRepository.java b/back/src/main/java/com/example/mistakes/service/ReadOnlyFsRepository.java index 3bb0e5c..737abff 100644 --- a/back/src/main/java/com/example/mistakes/service/ReadOnlyFsRepository.java +++ b/back/src/main/java/com/example/mistakes/service/ReadOnlyFsRepository.java @@ -1,7 +1,7 @@ package com.example.mistakes.service; import com.example.mistakes.base.type.FsMeta; -import com.example.mistakes.base.type.ID; +import com.example.mistakes.base.type.Identifiable; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -25,10 +25,10 @@ * @see org.springframework.data.repository.Repository */ @Repository -class ReadOnlyFsRepository - implements CrudRepository, QueryByExampleExecutor { +class ReadOnlyFsRepository & FsMeta, ID> + implements CrudRepository, QueryByExampleExecutor { - Map data = new HashMap<>(); + Map data = new HashMap<>(); @Override public @NonNull S save(@NonNull S entity) { @@ -37,7 +37,7 @@ class ReadOnlyFsRepository } @Override - public @NonNull Optional findById(@NonNull Number id) { + public @NonNull Optional findById(@NonNull ID id) { return Optional.ofNullable(data.get(id)); } @@ -96,13 +96,13 @@ public Iterable saveAll(Iterable entities) { } @Override - public boolean existsById(Number id) { + public boolean existsById(ID id) { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'existsById'"); } @Override - public Iterable findAllById(Iterable ids) { + public Iterable findAllById(Iterable ids) { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'findAllById'"); } @@ -114,7 +114,7 @@ public long count() { } @Override - public void deleteById(Number id) { + public void deleteById(ID id) { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'deleteById'"); } @@ -126,7 +126,7 @@ public void delete(T entity) { } @Override - public void deleteAllById(Iterable ids) { + public void deleteAllById(Iterable ids) { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'deleteAllById'"); } diff --git a/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java b/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java index 617f31e..ef23be7 100644 --- a/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java +++ b/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java @@ -23,24 +23,49 @@ class _01_OperationPriorityTests { @Autowired private QuestionService service; @Autowired private _01_OperationPriority target; + // need post-processing to remove redundant spaces + // i.g. double-spaces or tab or newline to single space + private String normalize(String code) { + return code.replaceAll("\\s+", " "); + } + @Test void testRegistration() { var classPath = "com/example/mistakes/expression/_01_OperationPriority.java"; var before = "int before(short lo, short hi) { return lo << 16 + hi; }"; var after = "int after(short lo, short hi) { return (lo << 16) + hi; }"; - var entity = service.findById(1); + var entity = service.findById("1"); assertEquals(entity.message(), classPath); - assertEquals(entity.getId(), 1); + assertEquals(entity.getId(), "1"); assertEquals(entity.getPath().toString(), "src/main/java/" + classPath); assertTrue(normalize(entity.getBefore()).contains(before)); assertTrue(normalize(entity.getAfter()).contains(after)); } - // need post-processing to remove redundant spaces - // i.g. double-spaces or tab or newline to single space - private String normalize(String code) { - return code.replaceAll("\\s+", " "); + @Test + void testEx2() { + var message = "com.example.mistakes.expression._01_OperationPriority.Ex2"; + var classPath = "com/example/mistakes/expression/_01_OperationPriority.java"; + var before = "int before() { return xmin + ymin << 8 + xmax << 16 + ymax << 24; }"; + var after = "int after() { return xmin + (ymin << 8) + (xmax << 16) + (ymax << 24); }"; + + // var entity = service.findAll().get(1); + // System.out.println(entity); + // System.out.println(entity.message()); + // System.out.println(entity.getId()); + // System.out.println(entity.getPath().toString()); + // System.out.println(entity.getBefore()); + // System.out.println(entity.getAfter()); + + var entity = service.findById("_01_2"); + // var entity = service.find(2, 2); + assertEquals(entity.message(), message); + assertEquals(entity.getId(), "_01_2"); + assertEquals(entity.getPath().toString(), "src/main/java/" + classPath); + + assertTrue(normalize(entity.getBefore()).contains(before)); + assertTrue(normalize(entity.getAfter()).contains(after)); } @ParameterizedTest From 69731cd0744ce828fb4ccb9e8cc804686c9545cb Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Wed, 19 Mar 2025 14:24:32 +0900 Subject: [PATCH 11/22] =?UTF-8?q?=E2=9C=A8=20Feat=20:=20ex1=20/=20ex2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../expression/_01_OperationPriority.java | 18 +++++++++--------- .../expression/_01_OperationPriorityTests.java | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java b/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java index 8c328cf..9491636 100644 --- a/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java +++ b/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java @@ -10,21 +10,21 @@ public class _01_OperationPriority { _01_OperationPriority(@Autowired QuestionService service) { - var builder = new QuestionEntityBuilder<_01_OperationPriority>(); - service.add(builder.build(_01_OperationPriority.class)); - + service.add(new QuestionEntityBuilder().build(Ex1.class)); service.add(new QuestionEntityBuilder().build(Ex2.class)); } - int before(short lo, short hi) { - return lo << 16 + hi; - } + static class Ex1 { + int before(short lo, short hi) { + return lo << 16 + hi; + } - int after(short lo, short hi) { - return (lo << 16) + hi; + int after(short lo, short hi) { + return (lo << 16) + hi; + } } - class Ex2 { + static class Ex2 { String subContext = "Binary Shift"; short xmin = 1, ymin = 1, xmax = 1, ymax = 1; diff --git a/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java b/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java index ef23be7..e34c6a6 100644 --- a/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java +++ b/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java @@ -21,7 +21,6 @@ class _01_OperationPriorityTests { @Autowired private QuestionService service; - @Autowired private _01_OperationPriority target; // need post-processing to remove redundant spaces // i.g. double-spaces or tab or newline to single space @@ -72,6 +71,7 @@ void testEx2() { @MethodSource("randomArguments") @DisplayName("Fuzz test with random inputs using ParameterizedTest") void fuzzTest(short lo, short hi) { + var target = new _01_OperationPriority.Ex1(); assertEquals( target.after(lo, hi), lo * Math.pow(2, 16) + hi, From 2b7456896e06f36e9dedabd98737115e8498bf10 Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Wed, 19 Mar 2025 17:46:11 +0900 Subject: [PATCH 12/22] =?UTF-8?q?=E2=9C=A8=20Feat=20:=20ends=20for=20each?= =?UTF-8?q?=20ex?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/questions/QuestionController.java | 31 +++++++++------- ...iority.java => _02_OperationPriority.java} | 4 +-- .../mistakes/service/QuestionServiceImpl.java | 5 +-- .../example/mistakes/api/BaseAPITests.java | 36 +++++++++++++++---- .../api/questions/QuestionAPITests.java | 2 +- ...s.java => _02_OperationPriorityTests.java} | 21 +++++------ 6 files changed, 65 insertions(+), 34 deletions(-) rename back/src/main/java/com/example/mistakes/expression/{_01_OperationPriority.java => _02_OperationPriority.java} (90%) rename back/src/test/java/com/example/mistakes/expression/{_01_OperationPriorityTests.java => _02_OperationPriorityTests.java} (83%) diff --git a/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java b/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java index 7a51c5c..bc079c0 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java +++ b/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java @@ -1,19 +1,18 @@ package com.example.mistakes.api.questions; - import com.example.mistakes.base.template.ResponseManyDTO; import com.example.mistakes.base.type.ResponseMany; import com.example.mistakes.service.QuestionService; - import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("/api/questions") +@RequestMapping("/api") public class QuestionController { @Autowired private final QuestionService service; @@ -21,21 +20,29 @@ public QuestionController(QuestionService service) { this.service = service; } - @GetMapping("/t1") - public ResponseEntity> t1() { - final var data = this.service.findAll(); - final var dto = new ResRecordQuestion(data, data.size()); + // TODO: assign endpoint for each example by id: _chpater_index + // GET /api/questions/1 -> _02_1 + // GET /api/expression/n -> _02_n + + @GetMapping("/questions/{id}") + public ResponseEntity> findOneByMistakeId( + @PathVariable("id") int id) { + // TODO: find by id should automatically handle the prefix + final var data = this.service.findAll().get(id - 1); + final var dto = new ResRecordQuestion(List.of(data), 1); return ResponseEntity.ok().body(dto); } - @GetMapping("/t2") - public ResponseEntity> t2() { - final var data = this.service.findAll(); - final var dto = new ResClassQuestion(data); + @GetMapping("/expression/{index}") + public ResponseEntity> findOneInExpressionByIndex( + @PathVariable("index") int index) { + final var data = this.service.findById("_02_%d".formatted(index)); + final var dto = new ResClassQuestion(List.of(data)); return ResponseEntity.ok().body(dto); } - @GetMapping("/t3") + // legacy + @GetMapping("/questions/t3") public ResponseEntity> t3() { final var data = this.service.findAll(); final var dto = ResClassOfQuestion.of(data); diff --git a/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java b/back/src/main/java/com/example/mistakes/expression/_02_OperationPriority.java similarity index 90% rename from back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java rename to back/src/main/java/com/example/mistakes/expression/_02_OperationPriority.java index 9491636..e8c9f97 100644 --- a/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java +++ b/back/src/main/java/com/example/mistakes/expression/_02_OperationPriority.java @@ -7,9 +7,9 @@ import org.springframework.stereotype.Component; @Component -public class _01_OperationPriority { +public class _02_OperationPriority { - _01_OperationPriority(@Autowired QuestionService service) { + _02_OperationPriority(@Autowired QuestionService service) { service.add(new QuestionEntityBuilder().build(Ex1.class)); service.add(new QuestionEntityBuilder().build(Ex2.class)); } diff --git a/back/src/main/java/com/example/mistakes/service/QuestionServiceImpl.java b/back/src/main/java/com/example/mistakes/service/QuestionServiceImpl.java index 9cfcc85..08d3b0e 100644 --- a/back/src/main/java/com/example/mistakes/service/QuestionServiceImpl.java +++ b/back/src/main/java/com/example/mistakes/service/QuestionServiceImpl.java @@ -2,6 +2,7 @@ import com.example.mistakes.api.questions.QuestionEntity; import java.util.List; +import java.util.NoSuchElementException; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -24,8 +25,8 @@ public QuestionEntity find(Number chapter, Number index) { } @Override - public QuestionEntity findById(String id) { - return repository.findById(id).orElse(null); + public QuestionEntity findById(String id) throws NoSuchElementException { + return repository.findById(id).orElseThrow(); } @Override diff --git a/back/src/test/java/com/example/mistakes/api/BaseAPITests.java b/back/src/test/java/com/example/mistakes/api/BaseAPITests.java index 3b92bb6..98c2561 100644 --- a/back/src/test/java/com/example/mistakes/api/BaseAPITests.java +++ b/back/src/test/java/com/example/mistakes/api/BaseAPITests.java @@ -12,6 +12,7 @@ import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; @@ -76,17 +77,38 @@ List getEndpoints() { return endpoints; } + @Test + void testLogEndpoints() { + log.info("Endpoints: {}", getEndpoints()); + // [ + // /v3/api-docs, /v3/api-docs.yaml, /error, /v3/api-docs/swagger-config, /error, + // /swagger-ui.html, + // /api/questions/t3, + // /api/questions/{id}, /api/hello, /api/expression/{index}] + } + @TestFactory Stream testSomeDynamicAsFactory() { - var urls = List.of("/api/questions/t1", "/api/questions/t2"); - var endpoints = getEndpoints(); - assert endpoints.containsAll(urls); + List endpoints = + List.of( + "/api/questions/t3", "/api/questions/{id}", "/api/hello", "/api/expression/{index}"); + + assert endpoints.containsAll(endpoints); return getEndpoints().stream() - .filter(e -> e.startsWith("/api/questions")) + .filter(e -> e.startsWith("/api") && !e.equals("/api/hello")) .map( - url -> - DynamicTest.dynamicTest( - url, () -> mockMvc.perform(get(url)).andExpectAll(matchers()))); + url -> { + String requestUrl = url.replace("{id}", "2").replace("{index}", "2"); + return DynamicTest.dynamicTest( + url, () -> mockMvc.perform(get(requestUrl)).andExpectAll(matchers())); + }); + + // return getEndpoints().stream() + // .filter(e -> e.startsWith("/api/questions")) + // .map( + // url -> + // DynamicTest.dynamicTest( + // url, () -> mockMvc.perform(get(url)).andExpectAll(matchers()))); } } diff --git a/back/src/test/java/com/example/mistakes/api/questions/QuestionAPITests.java b/back/src/test/java/com/example/mistakes/api/questions/QuestionAPITests.java index dd01830..e8971fb 100644 --- a/back/src/test/java/com/example/mistakes/api/questions/QuestionAPITests.java +++ b/back/src/test/java/com/example/mistakes/api/questions/QuestionAPITests.java @@ -20,7 +20,7 @@ class QuestionAPITests { void testGetQuestion() throws Exception { mockMvc - .perform(get("/api/questions/t1").accept(MediaType.APPLICATION_JSON)) + .perform(get("/api/questions/t3").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.result").exists()) diff --git a/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java b/back/src/test/java/com/example/mistakes/expression/_02_OperationPriorityTests.java similarity index 83% rename from back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java rename to back/src/test/java/com/example/mistakes/expression/_02_OperationPriorityTests.java index e34c6a6..69f341f 100644 --- a/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java +++ b/back/src/test/java/com/example/mistakes/expression/_02_OperationPriorityTests.java @@ -18,7 +18,7 @@ @SpringBootTest @RequiredArgsConstructor -class _01_OperationPriorityTests { +class _02_OperationPriorityTests { @Autowired private QuestionService service; @@ -30,12 +30,13 @@ private String normalize(String code) { @Test void testRegistration() { - var classPath = "com/example/mistakes/expression/_01_OperationPriority.java"; + var message = "com.example.mistakes.expression._02_OperationPriority.Ex1"; + var classPath = "com/example/mistakes/expression/_02_OperationPriority.java"; var before = "int before(short lo, short hi) { return lo << 16 + hi; }"; var after = "int after(short lo, short hi) { return (lo << 16) + hi; }"; - var entity = service.findById("1"); - assertEquals(entity.message(), classPath); - assertEquals(entity.getId(), "1"); + var entity = service.findById("_02_1"); + assertEquals(entity.getId(), "_02_1"); + assertEquals(entity.message(), message); assertEquals(entity.getPath().toString(), "src/main/java/" + classPath); assertTrue(normalize(entity.getBefore()).contains(before)); @@ -44,8 +45,8 @@ void testRegistration() { @Test void testEx2() { - var message = "com.example.mistakes.expression._01_OperationPriority.Ex2"; - var classPath = "com/example/mistakes/expression/_01_OperationPriority.java"; + var message = "com.example.mistakes.expression._02_OperationPriority.Ex2"; + var classPath = "com/example/mistakes/expression/_02_OperationPriority.java"; var before = "int before() { return xmin + ymin << 8 + xmax << 16 + ymax << 24; }"; var after = "int after() { return xmin + (ymin << 8) + (xmax << 16) + (ymax << 24); }"; @@ -57,10 +58,10 @@ void testEx2() { // System.out.println(entity.getBefore()); // System.out.println(entity.getAfter()); - var entity = service.findById("_01_2"); + var entity = service.findById("_02_2"); // var entity = service.find(2, 2); assertEquals(entity.message(), message); - assertEquals(entity.getId(), "_01_2"); + assertEquals(entity.getId(), "_02_2"); assertEquals(entity.getPath().toString(), "src/main/java/" + classPath); assertTrue(normalize(entity.getBefore()).contains(before)); @@ -71,7 +72,7 @@ void testEx2() { @MethodSource("randomArguments") @DisplayName("Fuzz test with random inputs using ParameterizedTest") void fuzzTest(short lo, short hi) { - var target = new _01_OperationPriority.Ex1(); + var target = new _02_OperationPriority.Ex1(); assertEquals( target.after(lo, hi), lo * Math.pow(2, 16) + hi, From 2d5d623246e8336355bb7b2018fbb885dc3a6a64 Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Wed, 19 Mar 2025 19:39:48 +0900 Subject: [PATCH 13/22] =?UTF-8?q?=E2=9C=A8=20Feat=20:=20refactor=20layouts?= =?UTF-8?q?,=20making=20components?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/components/GlobalStyles.astro | 15 +-- front/src/components/Navigator.astro | 125 ++++++++++++++++++++ front/src/components/PageHandler.astro | 83 +++++++++++++ front/src/components/core/CodeCard.astro | 57 +++++++++ front/src/components/core/CodeWrapper.astro | 51 ++++++++ front/src/layouts/ApiDataLayout.astro | 87 ++------------ front/src/layouts/HelloLayout.astro | 22 ++-- front/src/layouts/Landing.astro | 8 +- front/src/modules/getEndpoints.ts | 15 ++- 9 files changed, 362 insertions(+), 101 deletions(-) create mode 100644 front/src/components/Navigator.astro create mode 100644 front/src/components/PageHandler.astro create mode 100644 front/src/components/core/CodeCard.astro create mode 100644 front/src/components/core/CodeWrapper.astro diff --git a/front/src/components/GlobalStyles.astro b/front/src/components/GlobalStyles.astro index 768a9ec..209b112 100644 --- a/front/src/components/GlobalStyles.astro +++ b/front/src/components/GlobalStyles.astro @@ -1,24 +1,27 @@ + + diff --git a/front/src/components/PageHandler.astro b/front/src/components/PageHandler.astro new file mode 100644 index 0000000..5278837 --- /dev/null +++ b/front/src/components/PageHandler.astro @@ -0,0 +1,83 @@ +--- +// 현재 URL을 가져와서 경로를 분석합니다 +const url = Astro.url; +const pathSegments = url.pathname.split("/").filter(Boolean); +const lastSegment = pathSegments[pathSegments.length - 1]; + +// 마지막 세그먼트가 숫자인지 확인 +const isNumeric = /^\d+$/.test(lastSegment); +const currentPage = isNumeric ? parseInt(lastSegment) : null; + +// 이전/다음 페이지 URL 생성 +let prevPageUrl, nextPageUrl; +if (isNumeric && currentPage !== null) { + const basePath = pathSegments.slice(0, -1).join("/"); + prevPageUrl = currentPage > 1 ? `/${basePath}/${currentPage - 1}` : null; + nextPageUrl = `/${basePath}/${currentPage + 1}`; +} + +// 내비게이션 버튼이 표시되어야 하는지 결정 +const showNavigation = isNumeric && currentPage !== null; +--- + +{ + showNavigation && ( + + ) +} + + diff --git a/front/src/components/core/CodeCard.astro b/front/src/components/core/CodeCard.astro new file mode 100644 index 0000000..b83e9f9 --- /dev/null +++ b/front/src/components/core/CodeCard.astro @@ -0,0 +1,57 @@ +--- +import CodeWrapper from "./CodeWrapper.astro"; + +const { title, summary, code, lang } = Astro.props; +--- + +
+

{title}

+
{summary}
+ +
+ + diff --git a/front/src/components/core/CodeWrapper.astro b/front/src/components/core/CodeWrapper.astro new file mode 100644 index 0000000..0e4ce22 --- /dev/null +++ b/front/src/components/core/CodeWrapper.astro @@ -0,0 +1,51 @@ +--- +import { Code } from "astro:components"; + +const { code, lang } = Astro.props; + +const jsonify = (data: string) => JSON.stringify(data, null, 2); + +// 공통 들여쓰기 제거 +const trimIndents = (data: string) => { + // 줄 단위로 분리 + const lines = data.split("\n"); + + // 모든 줄의 공통 들여쓰기 최소값 찾기 (빈 줄은 무시) + let minIndent = Infinity; + for (const line of lines) { + // 공백만 있는 줄은 건너뛰기 + if (line.trim().length === 0) continue; + + // 앞쪽 공백 찾기 + const matched = line.match(/^(\s*)/); + if (!matched) continue; + + const indent = matched[1].length; + if (indent < minIndent) { + minIndent = indent; + } + } + + // 모든 줄이 빈 줄인 경우 예외 처리 + if (minIndent === Infinity) minIndent = 0; + + // 각 줄에서 공통 들여쓰기만 제거 + const trimmedLines = lines.map((line) => { + if (line.trim().length === 0) return line; + return line.substring(minIndent); + }); + + return trimmedLines.join("\n"); +}; + +const data = lang === "json" ? jsonify(code) : trimIndents(code); +--- + + diff --git a/front/src/layouts/ApiDataLayout.astro b/front/src/layouts/ApiDataLayout.astro index 2021433..7446cb3 100644 --- a/front/src/layouts/ApiDataLayout.astro +++ b/front/src/layouts/ApiDataLayout.astro @@ -1,6 +1,7 @@ --- +import CodeCard from "@/components/core/CodeCard.astro"; +import PageHandler from "@/components/PageHandler.astro"; import { fetchDataWithRetry } from "@/modules/fetchData"; -import { Code } from "astro:components"; export interface Props { apiPath: string; @@ -49,46 +50,16 @@ title = message -
-

API Response

-
GET /{apiPath}
- - -
- -
-

before

- - -
+ + + -
-

after

- - -
- + diff --git a/front/src/layouts/HelloLayout.astro b/front/src/layouts/HelloLayout.astro index b8b882c..9608934 100644 --- a/front/src/layouts/HelloLayout.astro +++ b/front/src/layouts/HelloLayout.astro @@ -1,6 +1,6 @@ --- +import CodeCard from "@/components/core/CodeCard.astro"; import { fetchDataWithRetry } from "@/modules/fetchData"; -import { Code } from "astro:components"; export interface Props { apiPath: string; @@ -36,20 +36,12 @@ let { message } = data; -
-

API Response

-
GET /{apiPath}
- - -
- + diff --git a/front/src/components/core/Link.astro b/front/src/components/core/Link.astro new file mode 100644 index 0000000..275fdab --- /dev/null +++ b/front/src/components/core/Link.astro @@ -0,0 +1,7 @@ +--- +const { href, rel, type } = Astro.props; +const baseUrl = import.meta.env.BASE_URL; +const extendedHref = `${baseUrl}${href}`.replaceAll("//", "/"); +--- + + diff --git a/front/src/layouts/Landing.astro b/front/src/layouts/Landing.astro index 3b81195..b032e95 100644 --- a/front/src/layouts/Landing.astro +++ b/front/src/layouts/Landing.astro @@ -1,4 +1,5 @@ --- +import Link from "@/components/core/Link.astro"; import Navigator from "@/components/Navigator.astro"; --- @@ -6,7 +7,7 @@ import Navigator from "@/components/Navigator.astro"; - + Astro Basics From 9c60595792e3d5b8eecaca27dde87329644f9df4 Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Thu, 20 Mar 2025 11:48:13 +0900 Subject: [PATCH 15/22] =?UTF-8?q?=E2=9C=A8=20Feat=20:=20BinaryShift=20-=20?= =?UTF-8?q?Ex4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../expression/_02_OperationPriority.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/back/src/main/java/com/example/mistakes/expression/_02_OperationPriority.java b/back/src/main/java/com/example/mistakes/expression/_02_OperationPriority.java index e8c9f97..b5a9a2a 100644 --- a/back/src/main/java/com/example/mistakes/expression/_02_OperationPriority.java +++ b/back/src/main/java/com/example/mistakes/expression/_02_OperationPriority.java @@ -15,6 +15,8 @@ public class _02_OperationPriority { } static class Ex1 { + String subContext = "Binary Shift"; + int before(short lo, short hi) { return lo << 16 + hi; } @@ -36,4 +38,29 @@ int after() { return xmin + (ymin << 8) + (xmax << 16) + (ymax << 24); } } + + static class Ex3 { + String subContext = "Binary Shift"; + int BLOCK_SIZE = 64 * 1024; + + int before() { + return BLOCK_SIZE + BLOCK_SIZE >> 2; + } + + int after() { + return BLOCK_SIZE + (BLOCK_SIZE >> 2); + } + } + + static class Ex4 { + String subContext = "Bitwise Operator"; + + int before(int bits) { + return bits & 0xFF00 + 1; + } + + int after(int bits) { + return (bits & 0xFF00) | 1; + } + } } From 28563a88b78bb56e288b66f144155983d2d159d5 Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Thu, 20 Mar 2025 11:48:19 +0900 Subject: [PATCH 16/22] =?UTF-8?q?=E2=9C=85=20Test=20:=20BinaryShift?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/README.md | 67 ++++++----------- .../_02_OperationPriorityTests.java | 73 ++++++++++++++----- 2 files changed, 79 insertions(+), 61 deletions(-) diff --git a/back/README.md b/back/README.md index bd423e6..3eada2b 100644 --- a/back/README.md +++ b/back/README.md @@ -5,47 +5,28 @@ - [`ktlint --format`](https://github.com/pinterest/ktlint?tab=readme-ov-file#quick-start) -com.example - ├── core - │ ├── domain1 - │ │ ├── subcontext1 - │ │ ├── subcontext2 - │ │ └── common - │ ├── domain2 - │ └── shared - │ └── metadata - ├── api - │ ├── controller - │ └── service - └── util - -TODO: refactor project structure - -TODO: add questions - - # 2. 표현식 Expression -1. 숫자 연산자 우선순위 오해 OperationPriority - - 이진 시프트 BinaryShift - - 비트 연산자 BitwiseOperator -2. 조건식의 괄호 누락 MissingParentheses - - &&, ||의 우선순위 LogicalOperatorPrecedence - - 조건 연산자와 덧셈 TernaryWithAddition - - 조건 연산자와 null 검사 TernaryWithNullCheck -3. 덧셈이 아닌 결합으로 작동 StringConcatenation -4. 멀티라인 문자열 리터럴 MultilineStringLiteral -5. 단항 덧셈 연산자 UnaryPlusOperator -6. 조건 표현식의 묵시적 타입 변환 ImplicitTypeConversion - - 조건 표현식의 박싱된 숫자 BoxedNumberConditional - - 중첩 조건 표현식 NestedConditional -7. 비단락 논리 연산자 사용 NonShortCircuitOperator -8. &&와 || 혼용 MixedLogicalOperators -9. 잘못된 가변 인수 호출 VarArgsIssues - - 모호한 가변 인수 호출 AmbiguousVarArgs - - 배열과 컬렉션 혼용 ArrayCollectionMixup - - 가변 인수에 원시 배열 전달 PrimitiveArrayToVarArgs -10. 조건 연산자와 가변 인수 호출 TernaryWithVarArgs -11. 반환값 무시 IgnoredReturnValue -12. 새롭게 생성된 객체를 사용하지 않음 UnusedObjects -13. 잘못된 메서드를 참조하는 바인딩 IncorrectMethodBinding -14. 메서드 참조 시 잘못된 메서드 지정 WrongMethodReference \ No newline at end of file +1. [ ] 숫자 연산자 우선순위 오해 OperationPriority + - [x] 이진 시프트 BinaryShift + - [ ] 비트 연산자 BitwiseOperator +2. [ ] 조건식의 괄호 누락 MissingParentheses + - [ ] &&, ||의 우선순위 LogicalOperatorPrecedence + - [ ] 조건 연산자와 덧셈 TernaryWithAddition + - [ ] 조건 연산자와 null 검사 TernaryWithNullCheck +3. [ ] 덧셈이 아닌 결합으로 작동 StringConcatenation +4. [ ] 멀티라인 문자열 리터럴 MultilineStringLiteral +5. [ ] 단항 덧셈 연산자 UnaryPlusOperator +6. [ ] 조건 표현식의 묵시적 타입 변환 ImplicitTypeConversion + - [ ] 조건 표현식의 박싱된 숫자 BoxedNumberConditional + - [ ] 중첩 조건 표현식 NestedConditional +7. [ ] 비단락 논리 연산자 사용 NonShortCircuitOperator +8. [ ] &&와 || 혼용 MixedLogicalOperators +9. [ ] 잘못된 가변 인수 호출 VarArgsIssues + - [ ] 모호한 가변 인수 호출 AmbiguousVarArgs + - [ ] 배열과 컬렉션 혼용 ArrayCollectionMixup + - [ ] 가변 인수에 원시 배열 전달 PrimitiveArrayToVarArgs +10. [ ] 조건 연산자와 가변 인수 호출 TernaryWithVarArgs +11. [ ] 반환값 무시 IgnoredReturnValue +12. [ ] 새롭게 생성된 객체를 사용하지 않음 UnusedObjects +13. [ ] 잘못된 메서드를 참조하는 바인딩 IncorrectMethodBinding +14. [ ] 메서드 참조 시 잘못된 메서드 지정 WrongMethodReference \ No newline at end of file diff --git a/back/src/test/java/com/example/mistakes/expression/_02_OperationPriorityTests.java b/back/src/test/java/com/example/mistakes/expression/_02_OperationPriorityTests.java index 69f341f..f4f00b4 100644 --- a/back/src/test/java/com/example/mistakes/expression/_02_OperationPriorityTests.java +++ b/back/src/test/java/com/example/mistakes/expression/_02_OperationPriorityTests.java @@ -5,10 +5,9 @@ import com.example.mistakes.api.questions.QuestionEntity; import com.example.mistakes.service.QuestionService; -import java.util.Random; +import java.util.List; import java.util.stream.Stream; import lombok.RequiredArgsConstructor; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -44,7 +43,7 @@ void testRegistration() { } @Test - void testEx2() { + void testRegistration2() { var message = "com.example.mistakes.expression._02_OperationPriority.Ex2"; var classPath = "com/example/mistakes/expression/_02_OperationPriority.java"; var before = "int before() { return xmin + ymin << 8 + xmax << 16 + ymax << 24; }"; @@ -69,23 +68,61 @@ void testEx2() { } @ParameterizedTest - @MethodSource("randomArguments") - @DisplayName("Fuzz test with random inputs using ParameterizedTest") - void fuzzTest(short lo, short hi) { + @MethodSource("dualCombinations") + void testEx1(short lo, short hi) { var target = new _02_OperationPriority.Ex1(); - assertEquals( - target.after(lo, hi), - lo * Math.pow(2, 16) + hi, - "Fuzz test failed with lo=%d hi=%d".formatted(lo, hi)); + int expected = lo * (int) Math.pow(2, 16) + hi; + assertEquals(expected, target.after(lo, hi), "Test failed with lo=%d hi=%d".formatted(lo, hi)); + } + + static Stream dualCombinations() { + var values = List.of((short) 0, (short) 1, (short) 2, Short.MAX_VALUE); + return values.stream().flatMap(lo -> values.stream().map(hi -> Arguments.of(lo, hi))); + } + + @Test + void testEx2() { + var target = new _02_OperationPriority.Ex2(); + + printBinary(1); + printBinary(1 + 1); + printBinary(1 + 1 << 8); + printBinary(1 + 1 << 8 + 1); + printBinary(1 + 1 << 8 + 1 << 16); + printBinary(1 + 1 << 8 + 1 << 16 + 1); + printBinary(1 + 1 << 8 + 1 << 16 + 1 << 24); + printBinary(target.after()); + + assertEquals(target.before(), 0); + } + + private static void printBinary(int value) { + printBinary(value, 32); + } + + private static void printBinary(int value, int length) { + System.out.println( + String.format("%" + length + "s", Integer.toBinaryString(value)).replace(' ', '0')); + } + + @Test + void testEx3() { + var target = new _02_OperationPriority.Ex3(); + + assertEquals(target.before(), target.BLOCK_SIZE * 0.5); + assertEquals(target.after(), target.BLOCK_SIZE * 1.25); } - static Stream randomArguments() { - Random random = new Random(); - return Stream.generate( - () -> - Arguments.of( - (short) random.nextInt(Short.MAX_VALUE), - (short) random.nextInt(Short.MAX_VALUE))) - .limit(100); + @Test + void testEx4() { + var target = new _02_OperationPriority.Ex4(); + var input = 0x0FF0; + + printBinary(input, 16); + printBinary(target.before(input), 16); + printBinary(target.after(input), 16); + + assertEquals(target.before(input) % 2, input % 2); + assertEquals(target.after(input) % 2, 1); } } From 5af3d47877cddd87f9b86116dd4e3530bd45103d Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Thu, 20 Mar 2025 20:21:14 +0900 Subject: [PATCH 17/22] =?UTF-8?q?=E2=9C=A8=20Feat=20:=20=5F02=5FMissingPar?= =?UTF-8?q?entheses?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/README.md | 12 ++-- .../api/questions/QuestionController.java | 2 +- .../api/questions/QuestionEntity.java | 21 +++++- ...iority.java => _01_OperationPriority.java} | 6 +- .../expression/_02_MissingParentheses.java | 72 +++++++++++++++++++ ...s.java => _01_OperationPriorityTests.java} | 27 +++---- .../_02_MissingParenthesesTests.java | 60 ++++++++++++++++ 7 files changed, 176 insertions(+), 24 deletions(-) rename back/src/main/java/com/example/mistakes/expression/{_02_OperationPriority.java => _01_OperationPriority.java} (85%) create mode 100644 back/src/main/java/com/example/mistakes/expression/_02_MissingParentheses.java rename back/src/test/java/com/example/mistakes/expression/{_02_OperationPriorityTests.java => _01_OperationPriorityTests.java} (84%) create mode 100644 back/src/test/java/com/example/mistakes/expression/_02_MissingParenthesesTests.java diff --git a/back/README.md b/back/README.md index 3eada2b..d19f4e7 100644 --- a/back/README.md +++ b/back/README.md @@ -6,13 +6,13 @@ # 2. 표현식 Expression -1. [ ] 숫자 연산자 우선순위 오해 OperationPriority +1. [x] 숫자 연산자 우선순위 오해 OperationPriority - [x] 이진 시프트 BinaryShift - - [ ] 비트 연산자 BitwiseOperator -2. [ ] 조건식의 괄호 누락 MissingParentheses - - [ ] &&, ||의 우선순위 LogicalOperatorPrecedence - - [ ] 조건 연산자와 덧셈 TernaryWithAddition - - [ ] 조건 연산자와 null 검사 TernaryWithNullCheck + - [x] 비트 연산자 BitwiseOperator +2. [x] 조건식의 괄호 누락 MissingParentheses + - [x] &&, ||의 우선순위 LogicalOperatorPrecedence + - [x] 조건 연산자와 덧셈 TernaryWithAddition + - [x] 조건 연산자와 null 검사 TernaryWithNullCheck 3. [ ] 덧셈이 아닌 결합으로 작동 StringConcatenation 4. [ ] 멀티라인 문자열 리터럴 MultilineStringLiteral 5. [ ] 단항 덧셈 연산자 UnaryPlusOperator diff --git a/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java b/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java index bc079c0..bdc27ba 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java +++ b/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java @@ -36,7 +36,7 @@ public ResponseEntity> findOneByMistakeId( @GetMapping("/expression/{index}") public ResponseEntity> findOneInExpressionByIndex( @PathVariable("index") int index) { - final var data = this.service.findById("_02_%d".formatted(index)); + final var data = this.service.findById("_01_%d".formatted(index)); final var dto = new ResClassQuestion(List.of(data)); return ResponseEntity.ok().body(dto); } diff --git a/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java b/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java index e615b54..cb3cad5 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java +++ b/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java @@ -8,24 +8,41 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.extern.log4j.Log4j2; +// TODO: refactor to class @Log4j2 public record QuestionEntity(String message) implements Message, Identifiable, FsMeta { + public Number getChapter() { + var data = new HashMap(); + data.put("expression", 2); + + var topLevelPackage = message.split("\\.")[3]; + + return data.getOrDefault(topLevelPackage, 0); + } + public String getId() { + // TODO: refactor using getChapter() + var data = new HashMap(); + data.put("expression", 2); + var topLevelPackage = message.split("\\.")[3]; + var chapter = data.getOrDefault(topLevelPackage, 0); + // new: return 1-2 when got _01_OperationPriority // when getting `com.example.mistakes.expression._01_OperationPriority.Ex2` // return id = "_01_2" Pattern pattern = Pattern.compile("_(\\d+)_\\S+(\\d+)"); Matcher matcher = pattern.matcher(message); if (matcher.find()) { - String chapter = matcher.group(1); + String mistakeId = matcher.group(1); String index = matcher.group(2); - return "_%s_%s".formatted(chapter, index); + return "_%s_%s".formatted(mistakeId, index); } // legacy: return 1 when got `com/example/mistakes/expression/_01_OperationPriority.java` diff --git a/back/src/main/java/com/example/mistakes/expression/_02_OperationPriority.java b/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java similarity index 85% rename from back/src/main/java/com/example/mistakes/expression/_02_OperationPriority.java rename to back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java index b5a9a2a..50c05a9 100644 --- a/back/src/main/java/com/example/mistakes/expression/_02_OperationPriority.java +++ b/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java @@ -7,11 +7,13 @@ import org.springframework.stereotype.Component; @Component -public class _02_OperationPriority { +public class _01_OperationPriority { - _02_OperationPriority(@Autowired QuestionService service) { + _01_OperationPriority(@Autowired QuestionService service) { service.add(new QuestionEntityBuilder().build(Ex1.class)); service.add(new QuestionEntityBuilder().build(Ex2.class)); + service.add(new QuestionEntityBuilder().build(Ex3.class)); + service.add(new QuestionEntityBuilder().build(Ex4.class)); } static class Ex1 { diff --git a/back/src/main/java/com/example/mistakes/expression/_02_MissingParentheses.java b/back/src/main/java/com/example/mistakes/expression/_02_MissingParentheses.java new file mode 100644 index 0000000..e2aef43 --- /dev/null +++ b/back/src/main/java/com/example/mistakes/expression/_02_MissingParentheses.java @@ -0,0 +1,72 @@ +package com.example.mistakes.expression; + +import static java.util.Objects.requireNonNullElse; + +import com.example.mistakes.QuestionEntityBuilder; +import com.example.mistakes.api.questions.QuestionEntity; +import com.example.mistakes.service.QuestionService; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class _02_MissingParentheses { + + _02_MissingParentheses(@Autowired QuestionService service) { + service.add(new QuestionEntityBuilder().build(Ex1.class)); + service.add(new QuestionEntityBuilder().build(Ex2.class)); + service.add(new QuestionEntityBuilder().build(Ex3.class)); + service.add(new QuestionEntityBuilder().build(Ex4.class)); + } + + static class Ex1 { + String subContext = "Logical operator precedence"; + + Boolean before(int index, String str) { + return index >= 0 && str.charAt(index) == ' ' || str.charAt(index) == '\t'; + } + + Boolean after(int index, String str) { + return index >= 0 && (str.charAt(index) == ' ' || str.charAt(index) == '\t'); + } + } + + static class Ex2 { + String subContext = "Ternary & addition"; + + StringBuilder before(String str, int indent) { + int capacity = str.length() + indent < 0 ? 0 : indent; + return new StringBuilder(capacity); + } + + StringBuilder after(String str, int indent) { + int capacity = str.length() + Math.max(indent, 0); + return new StringBuilder(capacity); + } + } + + static class Ex3 { + String subContext = "Ternary & null check"; + + String before(String value) { + return "Value: " + value != null ? value : "(unknown)"; + } + + String after(String value) { + return "Value: " + requireNonNullElse(value, "(unknown)"); + } + } + + static class Ex4 { + String subContext = "Ternary & null check"; + + int before(List input, String newItem) { + return input.size() + newItem == null ? 0 : 1; + } + + int after(List input, String newItem) { + int additionalElements = newItem == null ? 0 : 1; + return input.size() + additionalElements; + } + } +} diff --git a/back/src/test/java/com/example/mistakes/expression/_02_OperationPriorityTests.java b/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java similarity index 84% rename from back/src/test/java/com/example/mistakes/expression/_02_OperationPriorityTests.java rename to back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java index f4f00b4..79eb32f 100644 --- a/back/src/test/java/com/example/mistakes/expression/_02_OperationPriorityTests.java +++ b/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java @@ -17,7 +17,7 @@ @SpringBootTest @RequiredArgsConstructor -class _02_OperationPriorityTests { +class _01_OperationPriorityTests { @Autowired private QuestionService service; @@ -27,14 +27,15 @@ private String normalize(String code) { return code.replaceAll("\\s+", " "); } + // TODO: move to BaseAPITests @Test void testRegistration() { - var message = "com.example.mistakes.expression._02_OperationPriority.Ex1"; - var classPath = "com/example/mistakes/expression/_02_OperationPriority.java"; + var message = "com.example.mistakes.expression._01_OperationPriority.Ex1"; + var classPath = "com/example/mistakes/expression/_01_OperationPriority.java"; var before = "int before(short lo, short hi) { return lo << 16 + hi; }"; var after = "int after(short lo, short hi) { return (lo << 16) + hi; }"; - var entity = service.findById("_02_1"); - assertEquals(entity.getId(), "_02_1"); + var entity = service.findById("_01_1"); + assertEquals(entity.getId(), "_01_1"); assertEquals(entity.message(), message); assertEquals(entity.getPath().toString(), "src/main/java/" + classPath); @@ -44,8 +45,8 @@ void testRegistration() { @Test void testRegistration2() { - var message = "com.example.mistakes.expression._02_OperationPriority.Ex2"; - var classPath = "com/example/mistakes/expression/_02_OperationPriority.java"; + var message = "com.example.mistakes.expression._01_OperationPriority.Ex2"; + var classPath = "com/example/mistakes/expression/_01_OperationPriority.java"; var before = "int before() { return xmin + ymin << 8 + xmax << 16 + ymax << 24; }"; var after = "int after() { return xmin + (ymin << 8) + (xmax << 16) + (ymax << 24); }"; @@ -57,10 +58,10 @@ void testRegistration2() { // System.out.println(entity.getBefore()); // System.out.println(entity.getAfter()); - var entity = service.findById("_02_2"); + var entity = service.findById("_01_2"); // var entity = service.find(2, 2); assertEquals(entity.message(), message); - assertEquals(entity.getId(), "_02_2"); + assertEquals(entity.getId(), "_01_2"); assertEquals(entity.getPath().toString(), "src/main/java/" + classPath); assertTrue(normalize(entity.getBefore()).contains(before)); @@ -70,7 +71,7 @@ void testRegistration2() { @ParameterizedTest @MethodSource("dualCombinations") void testEx1(short lo, short hi) { - var target = new _02_OperationPriority.Ex1(); + var target = new _01_OperationPriority.Ex1(); int expected = lo * (int) Math.pow(2, 16) + hi; assertEquals(expected, target.after(lo, hi), "Test failed with lo=%d hi=%d".formatted(lo, hi)); } @@ -82,7 +83,7 @@ static Stream dualCombinations() { @Test void testEx2() { - var target = new _02_OperationPriority.Ex2(); + var target = new _01_OperationPriority.Ex2(); printBinary(1); printBinary(1 + 1); @@ -107,7 +108,7 @@ private static void printBinary(int value, int length) { @Test void testEx3() { - var target = new _02_OperationPriority.Ex3(); + var target = new _01_OperationPriority.Ex3(); assertEquals(target.before(), target.BLOCK_SIZE * 0.5); assertEquals(target.after(), target.BLOCK_SIZE * 1.25); @@ -115,7 +116,7 @@ void testEx3() { @Test void testEx4() { - var target = new _02_OperationPriority.Ex4(); + var target = new _01_OperationPriority.Ex4(); var input = 0x0FF0; printBinary(input, 16); diff --git a/back/src/test/java/com/example/mistakes/expression/_02_MissingParenthesesTests.java b/back/src/test/java/com/example/mistakes/expression/_02_MissingParenthesesTests.java new file mode 100644 index 0000000..a8fd84c --- /dev/null +++ b/back/src/test/java/com/example/mistakes/expression/_02_MissingParenthesesTests.java @@ -0,0 +1,60 @@ +package com.example.mistakes.expression; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +@RequiredArgsConstructor +class _02_MissingParenthesesTests { + + @Test + void testEx1() { + var target = new _02_MissingParentheses.Ex1(); + + assertThrows(StringIndexOutOfBoundsException.class, () -> target.before(-1, " ")); + assertDoesNotThrow(() -> target.after(-1, " ")); + + assertTrue(target.after(0, "\t")); + assertTrue(target.after(1, "a ")); + } + + @Test + void testEx2() { + var target = new _02_MissingParentheses.Ex2(); + + assertThrows(NegativeArraySizeException.class, () -> target.before("abc", -1)); + assertDoesNotThrow(() -> target.after("abc", -1)); + + assertTrue(target.after("abc", 0).capacity() == 3); + } + + @Test + void testEx3() { + var target = new _02_MissingParentheses.Ex3(); + + var whenNull = "Value: (unknown)"; + assertNotEquals(target.before(null), whenNull); + assertEquals(target.after(null), whenNull); + } + + @Test + void testEx4() { + var target = new _02_MissingParentheses.Ex4(); + + var init = List.of("a", "b", "c"); + + assertEquals(target.before(init, null), 1); + assertEquals(target.before(init, "d"), 1); + + assertEquals(target.after(init, null), 3); + assertEquals(target.after(init, "d"), 4); + } +} From c1064e7e29c1ffa1aeabb82b6c43e9479323c584 Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Fri, 21 Mar 2025 00:28:32 +0900 Subject: [PATCH 18/22] =?UTF-8?q?=F0=9F=94=84=20Refactor=20:=20controller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/questions/ChaptersController.java | 49 ++++++++++++ .../api/questions/ExamplesController.java | 37 +++++++++ .../api/questions/QuestionController.java | 76 ------------------- .../api/questions/QuestionEntity.java | 9 ++- 4 files changed, 94 insertions(+), 77 deletions(-) create mode 100644 back/src/main/java/com/example/mistakes/api/questions/ChaptersController.java create mode 100644 back/src/main/java/com/example/mistakes/api/questions/ExamplesController.java delete mode 100644 back/src/main/java/com/example/mistakes/api/questions/QuestionController.java diff --git a/back/src/main/java/com/example/mistakes/api/questions/ChaptersController.java b/back/src/main/java/com/example/mistakes/api/questions/ChaptersController.java new file mode 100644 index 0000000..5ee9124 --- /dev/null +++ b/back/src/main/java/com/example/mistakes/api/questions/ChaptersController.java @@ -0,0 +1,49 @@ +package com.example.mistakes.api.questions; + +import com.example.mistakes.base.type.ResponseMany; +import com.example.mistakes.service.QuestionService; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api") +public class ChaptersController { + @Autowired private final QuestionService service; + + public ChaptersController(QuestionService service) { + this.service = service; + } + + private ResponseEntity> _response(Iterable data) { + final var dto = new ResClassQuestion(data); + return ResponseEntity.ok().body(dto); + } + + @GetMapping("/{chapterName}") + public ResponseEntity> getAllMistakesInChapter( + @PathVariable String chapterName) { + final var data = this.service.findAllByChapterName(chapterName); + return _response(data); + } + + @GetMapping("/{chapterName}/{mistakeId}") + public ResponseEntity> getExamplesInMistake( + @PathVariable String chapterName, @PathVariable Integer mistakeId) { + final var data = this.service.findAllByChapterName(chapterName); + return _response(data); + } + + @GetMapping("/{chapterName}/{mistakeId}/{exampleId}") + public ResponseEntity> getOneExample( + @PathVariable String chapterName, + @PathVariable Integer mistakeId, + @PathVariable Integer exampleId) { + final var data = this.service.findOne(chapterName, mistakeId, exampleId); + return _response(List.of(data)); + } +} diff --git a/back/src/main/java/com/example/mistakes/api/questions/ExamplesController.java b/back/src/main/java/com/example/mistakes/api/questions/ExamplesController.java new file mode 100644 index 0000000..6909dc0 --- /dev/null +++ b/back/src/main/java/com/example/mistakes/api/questions/ExamplesController.java @@ -0,0 +1,37 @@ +package com.example.mistakes.api.questions; + +import com.example.mistakes.base.type.ResponseMany; +import com.example.mistakes.service.QuestionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/examples") +public class ExamplesController { + @Autowired private final QuestionService service; + + public ExamplesController(QuestionService service) { + this.service = service; + } + + private ResponseEntity> _response(Iterable data) { + final var dto = new ResClassQuestion(data); + return ResponseEntity.ok().body(dto); + } + + @GetMapping("/") + public ResponseEntity> getAll() { + final var data = this.service.findAll(); + return _response(data); + } + + @GetMapping("/{id}") + public ResponseEntity> getOneById(@PathVariable("id") Integer id) { + final var data = this.service.findAllByMistakeId(id); + return _response(data); + } +} diff --git a/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java b/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java deleted file mode 100644 index bdc27ba..0000000 --- a/back/src/main/java/com/example/mistakes/api/questions/QuestionController.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.example.mistakes.api.questions; - -import com.example.mistakes.base.template.ResponseManyDTO; -import com.example.mistakes.base.type.ResponseMany; -import com.example.mistakes.service.QuestionService; -import java.util.List; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/api") -public class QuestionController { - @Autowired private final QuestionService service; - - public QuestionController(QuestionService service) { - this.service = service; - } - - // TODO: assign endpoint for each example by id: _chpater_index - // GET /api/questions/1 -> _02_1 - // GET /api/expression/n -> _02_n - - @GetMapping("/questions/{id}") - public ResponseEntity> findOneByMistakeId( - @PathVariable("id") int id) { - // TODO: find by id should automatically handle the prefix - final var data = this.service.findAll().get(id - 1); - final var dto = new ResRecordQuestion(List.of(data), 1); - return ResponseEntity.ok().body(dto); - } - - @GetMapping("/expression/{index}") - public ResponseEntity> findOneInExpressionByIndex( - @PathVariable("index") int index) { - final var data = this.service.findById("_01_%d".formatted(index)); - final var dto = new ResClassQuestion(List.of(data)); - return ResponseEntity.ok().body(dto); - } - - // legacy - @GetMapping("/questions/t3") - public ResponseEntity> t3() { - final var data = this.service.findAll(); - final var dto = ResClassOfQuestion.of(data); - return ResponseEntity.ok().body(dto); - } -} - -// Type 1: record -record ResRecordQuestion(List result, Integer length) - implements ResponseMany {} - -// Type 2: custom wrapper DTO -final class ResClassQuestion extends ResponseManyDTO { - ResClassQuestion(Iterable result) { - super(result); - } -} - -// Type 3: custom wrapper DTO with static of -final class ResClassOfQuestion { - - private static class innerImpl extends ResponseManyDTO { - innerImpl(Iterable result) { - super(result); - } - } - - static ResponseManyDTO of(Iterable result) { - return new innerImpl(result); - } -} diff --git a/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java b/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java index cb3cad5..b4b8db5 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java +++ b/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java @@ -1,5 +1,6 @@ package com.example.mistakes.api.questions; +import com.example.mistakes.base.template.ResponseManyDTO; import com.example.mistakes.base.type.FsMeta; import com.example.mistakes.base.type.Identifiable; import com.example.mistakes.base.type.Message; @@ -14,6 +15,12 @@ import java.util.regex.Pattern; import lombok.extern.log4j.Log4j2; +final class ResClassQuestion extends ResponseManyDTO { + ResClassQuestion(Iterable result) { + super(result); + } +} + // TODO: refactor to class @Log4j2 public record QuestionEntity(String message) implements Message, Identifiable, FsMeta { @@ -42,7 +49,7 @@ public String getId() { if (matcher.find()) { String mistakeId = matcher.group(1); String index = matcher.group(2); - return "_%s_%s".formatted(mistakeId, index); + return "%s_%s_%s".formatted(chapter, mistakeId, index); } // legacy: return 1 when got `com/example/mistakes/expression/_01_OperationPriority.java` From 4adac07f2cab27b50ee4538d464690007acb3e74 Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Fri, 21 Mar 2025 00:28:58 +0900 Subject: [PATCH 19/22] =?UTF-8?q?=F0=9F=94=84=20Refactor=20:=20service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mistakes/service/QuestionService.java | 31 ++++--- .../mistakes/service/QuestionServiceImpl.java | 60 +++++++++----- .../service/ReadOnlyFsRepository.java | 83 ++++++------------- 3 files changed, 77 insertions(+), 97 deletions(-) diff --git a/back/src/main/java/com/example/mistakes/service/QuestionService.java b/back/src/main/java/com/example/mistakes/service/QuestionService.java index c164e97..7f408a2 100644 --- a/back/src/main/java/com/example/mistakes/service/QuestionService.java +++ b/back/src/main/java/com/example/mistakes/service/QuestionService.java @@ -1,30 +1,27 @@ package com.example.mistakes.service; +import com.example.mistakes.api.questions.QuestionEntity; import java.util.List; -public interface QuestionService { +public interface QuestionService { + // Register + void add(QuestionEntity entity); - void add(T entity); + void addAll(Iterable entity); - List findAll(); + // find one + QuestionEntity findOne(String id); - T find(Number chapter, Number index); + QuestionEntity findOne(String chapterName, Integer mistakeId, Integer exampleId); - // 1. query by question id - T findById(String id); + QuestionEntity findOne(Integer chapterNumber, Integer mistakeId, Integer exampleId); - // 2. chapter - List findByChapter(String name); + // find many + List findAll(); - List findByChapter(Number index); + List findAllByChapterName(String name); - // 3. keywords - enum ConditionType { - AND, - OR, - } + List findAllByChapterNumber(Number number); - List findByKeywords(String keyword); - - List findByKeywords(Iterable keywords, ConditionType type); + List findAllByMistakeId(Number id); } diff --git a/back/src/main/java/com/example/mistakes/service/QuestionServiceImpl.java b/back/src/main/java/com/example/mistakes/service/QuestionServiceImpl.java index 08d3b0e..c3a0e1d 100644 --- a/back/src/main/java/com/example/mistakes/service/QuestionServiceImpl.java +++ b/back/src/main/java/com/example/mistakes/service/QuestionServiceImpl.java @@ -1,7 +1,9 @@ package com.example.mistakes.service; import com.example.mistakes.api.questions.QuestionEntity; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; @@ -9,54 +11,70 @@ @Service @RequiredArgsConstructor -public class QuestionServiceImpl implements QuestionService { +public class QuestionServiceImpl implements QuestionService { @Autowired private final ReadOnlyFsRepository repository; + Map chpaterMap = new HashMap<>(Map.of("expression", 2)); + + private Integer _convertChapterNameToNumber(String name) throws NoSuchElementException { + if (!chpaterMap.containsKey(name)) { + throw new NoSuchElementException("Chapter not found: %s".formatted(name)); + } + return chpaterMap.get(name); + } + @Override public void add(QuestionEntity entity) { repository.save(entity); } @Override - public QuestionEntity find(Number chapter, Number index) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'find'"); + public void addAll(Iterable entity) { + repository.saveAll(entity); } @Override - public QuestionEntity findById(String id) throws NoSuchElementException { + public QuestionEntity findOne(String id) { return repository.findById(id).orElseThrow(); } @Override - public List findAll() { - List res = new java.util.ArrayList(); - repository.findAll().forEach(res::add); - return res; + public QuestionEntity findOne(String chapterName, Integer mistakeId, Integer exampleId) { + var chapterNumber = _convertChapterNameToNumber(chapterName); + return findOne(chapterNumber, mistakeId, exampleId); + } + + @Override + public QuestionEntity findOne(Integer chapterNumber, Integer mistakeId, Integer exampleId) { + var id = "%d_%02d_%d".formatted(chapterNumber, mistakeId, exampleId); + return findOne(id); } @Override - public List findByChapter(String name) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'findByChapter'"); + public List findAll() { + return (List) repository.findAll(); } @Override - public List findByChapter(Number index) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'findByChapter'"); + public List findAllByChapterName(String name) { + var chapterNumber = _convertChapterNameToNumber(name); + return findAllByChapterNumber(chapterNumber); } @Override - public List findByKeywords(String keyword) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'findByKeywords'"); + public List findAllByChapterNumber(Number number) { + var regex = "%d_\\d+_\\d+".formatted(number); + return _findAllByPattern(regex); } @Override - public List findByKeywords(Iterable keywords, ConditionType type) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'findByKeywords'"); + public List findAllByMistakeId(Number id) { + var regex = "\\d+_%02d_\\d+".formatted(id); + return _findAllByPattern(regex); + } + + private List _findAllByPattern(String regex) { + return (List) repository.findAllByPattern(regex); } } diff --git a/back/src/main/java/com/example/mistakes/service/ReadOnlyFsRepository.java b/back/src/main/java/com/example/mistakes/service/ReadOnlyFsRepository.java index 737abff..ab29034 100644 --- a/back/src/main/java/com/example/mistakes/service/ReadOnlyFsRepository.java +++ b/back/src/main/java/com/example/mistakes/service/ReadOnlyFsRepository.java @@ -3,16 +3,14 @@ import com.example.mistakes.base.type.FsMeta; import com.example.mistakes.base.type.Identifiable; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; -import java.util.function.Function; -import org.springframework.data.domain.Example; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; -import org.springframework.data.repository.query.QueryByExampleExecutor; import org.springframework.lang.NonNull; import org.springframework.stereotype.Repository; @@ -26,7 +24,7 @@ */ @Repository class ReadOnlyFsRepository & FsMeta, ID> - implements CrudRepository, QueryByExampleExecutor { + implements CrudRepository { Map data = new HashMap<>(); @@ -37,62 +35,35 @@ class ReadOnlyFsRepository & FsMeta, ID> } @Override - public @NonNull Optional findById(@NonNull ID id) { - return Optional.ofNullable(data.get(id)); - } - - @Override - public @NonNull Iterable findAll() { - return data.values(); - } - - @Override - public Optional findOne(Example example) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'findOne'"); - } - - @Override - public Iterable findAll(Example example) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'findAll'"); - } - - @Override - public Iterable findAll(Example example, Sort sort) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'findAll'"); + public @NonNull Iterable saveAll(@NonNull Iterable entities) { + // use parallelStream() if entities came as a Collection and have many elements + entities.forEach(this::save); + return entities; } @Override - public Page findAll(Example example, Pageable pageable) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'findAll'"); - } - - @Override - public long count(Example example) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'count'"); + public @NonNull Optional findById(@NonNull ID id) { + return Optional.ofNullable(data.get(id)); } @Override - public boolean exists(Example example) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'exists'"); + public @NonNull Iterable findAll() { + return List.copyOf(data.values()); } @Override - public R findBy( - Example example, Function, R> queryFunction) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'findBy'"); + public @NonNull Iterable findAllById(@NonNull Iterable ids) { + return StreamSupport.stream(ids.spliterator(), true) + .map(id -> data.get(id)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); } - @Override - public Iterable saveAll(Iterable entities) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'saveAll'"); + public @NonNull Iterable findAllByPattern(@NonNull String regex) { + Pattern pattern = Pattern.compile(regex); + return data.values().parallelStream() + .filter(entity -> pattern.matcher(entity.getId().toString()).matches()) + .collect(Collectors.toList()); } @Override @@ -101,12 +72,6 @@ public boolean existsById(ID id) { throw new UnsupportedOperationException("Unimplemented method 'existsById'"); } - @Override - public Iterable findAllById(Iterable ids) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'findAllById'"); - } - @Override public long count() { // TODO Auto-generated method stub From b5dd8f2b6bbcf84d13e4c949ddba6783655710b2 Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Fri, 21 Mar 2025 00:29:34 +0900 Subject: [PATCH 20/22] =?UTF-8?q?=F0=9F=94=84=20Refactor=20:=20QuestionEnt?= =?UTF-8?q?ityBuilder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/mistakes/QuestionEntityBuilder.java | 10 ++++++++++ .../mistakes/expression/_01_OperationPriority.java | 9 +++------ .../mistakes/expression/_02_MissingParentheses.java | 9 +++------ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/back/src/main/java/com/example/mistakes/QuestionEntityBuilder.java b/back/src/main/java/com/example/mistakes/QuestionEntityBuilder.java index c3ba248..30ab115 100644 --- a/back/src/main/java/com/example/mistakes/QuestionEntityBuilder.java +++ b/back/src/main/java/com/example/mistakes/QuestionEntityBuilder.java @@ -1,9 +1,19 @@ package com.example.mistakes; import com.example.mistakes.api.questions.QuestionEntity; +import java.util.List; +import java.util.stream.Collectors; public class QuestionEntityBuilder { public QuestionEntity build(Class cls) { return new QuestionEntity(cls.getCanonicalName()); } + + public static QuestionEntity of(Class cls) { + return new QuestionEntity(cls.getCanonicalName()); + } + + public static Iterable of(Class... cls) { + return List.of(cls).stream().map(QuestionEntityBuilder::of).collect(Collectors.toList()); + } } diff --git a/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java b/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java index 50c05a9..4ba1721 100644 --- a/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java +++ b/back/src/main/java/com/example/mistakes/expression/_01_OperationPriority.java @@ -1,7 +1,6 @@ package com.example.mistakes.expression; import com.example.mistakes.QuestionEntityBuilder; -import com.example.mistakes.api.questions.QuestionEntity; import com.example.mistakes.service.QuestionService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -9,11 +8,9 @@ @Component public class _01_OperationPriority { - _01_OperationPriority(@Autowired QuestionService service) { - service.add(new QuestionEntityBuilder().build(Ex1.class)); - service.add(new QuestionEntityBuilder().build(Ex2.class)); - service.add(new QuestionEntityBuilder().build(Ex3.class)); - service.add(new QuestionEntityBuilder().build(Ex4.class)); + _01_OperationPriority(@Autowired QuestionService service) { + var entities = QuestionEntityBuilder.of(Ex1.class, Ex2.class, Ex3.class, Ex4.class); + service.addAll(entities); } static class Ex1 { diff --git a/back/src/main/java/com/example/mistakes/expression/_02_MissingParentheses.java b/back/src/main/java/com/example/mistakes/expression/_02_MissingParentheses.java index e2aef43..858f2fe 100644 --- a/back/src/main/java/com/example/mistakes/expression/_02_MissingParentheses.java +++ b/back/src/main/java/com/example/mistakes/expression/_02_MissingParentheses.java @@ -3,7 +3,6 @@ import static java.util.Objects.requireNonNullElse; import com.example.mistakes.QuestionEntityBuilder; -import com.example.mistakes.api.questions.QuestionEntity; import com.example.mistakes.service.QuestionService; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; @@ -12,11 +11,9 @@ @Component public class _02_MissingParentheses { - _02_MissingParentheses(@Autowired QuestionService service) { - service.add(new QuestionEntityBuilder().build(Ex1.class)); - service.add(new QuestionEntityBuilder().build(Ex2.class)); - service.add(new QuestionEntityBuilder().build(Ex3.class)); - service.add(new QuestionEntityBuilder().build(Ex4.class)); + _02_MissingParentheses(@Autowired QuestionService service) { + var entities = QuestionEntityBuilder.of(Ex1.class, Ex2.class, Ex3.class, Ex4.class); + service.addAll(entities); } static class Ex1 { From 80d063a94348e72712068d37289ab68aa196481c Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Fri, 21 Mar 2025 00:29:52 +0900 Subject: [PATCH 21/22] =?UTF-8?q?=F0=9F=90=9B=20Fix=20:=20update=20on=20ch?= =?UTF-8?q?anges?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/mistakes/api/BaseAPITests.java | 65 ++++++++++--------- .../_01_OperationPriorityTests.java | 11 ++-- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/back/src/test/java/com/example/mistakes/api/BaseAPITests.java b/back/src/test/java/com/example/mistakes/api/BaseAPITests.java index 98c2561..8f39f93 100644 --- a/back/src/test/java/com/example/mistakes/api/BaseAPITests.java +++ b/back/src/test/java/com/example/mistakes/api/BaseAPITests.java @@ -1,6 +1,7 @@ package com.example.mistakes.api; import static org.hamcrest.Matchers.greaterThan; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -8,6 +9,7 @@ import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.function.Function; import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; @@ -33,15 +35,6 @@ class BaseAPITests { @Autowired private MockMvc mockMvc; @Autowired private RequestMappingHandlerMapping handlerMapping; - ResultMatcher[] matchers() { - return new ResultMatcher[] { - status().isOk(), - content().contentType(MediaType.APPLICATION_JSON), - jsonPath("$.result").exists(), - jsonPath("$.length").value(greaterThan(0)) - }; - } - /** * Extract `GET /api/**` endpoints from handlerMapping * @@ -78,37 +71,49 @@ List getEndpoints() { } @Test - void testLogEndpoints() { - log.info("Endpoints: {}", getEndpoints()); - // [ - // /v3/api-docs, /v3/api-docs.yaml, /error, /v3/api-docs/swagger-config, /error, - // /swagger-ui.html, - // /api/questions/t3, - // /api/questions/{id}, /api/hello, /api/expression/{index}] + void testEndPoints() { + List endpoints = + List.of( + "/error", + // swagger + "/v3/api-docs.yaml", + "/v3/api-docs", + "/v3/api-docs/swagger-config", + "/swagger-ui.html", + // examples + "/api/examples/", + "/api/examples/{id}", + // chapters + "/api/{chapterName}/{mistakeId}/{exampleId}", + "/api/{chapterName}", + "/api/{chapterName}/{mistakeId}", + // legacy + "/api/hello"); + + assertEquals(Set.copyOf(endpoints), Set.copyOf(getEndpoints())); } @TestFactory Stream testSomeDynamicAsFactory() { - List endpoints = - List.of( - "/api/questions/t3", "/api/questions/{id}", "/api/hello", "/api/expression/{index}"); - - assert endpoints.containsAll(endpoints); + var matchers = + new ResultMatcher[] { + status().isOk(), + content().contentType(MediaType.APPLICATION_JSON), + jsonPath("$.result").exists(), + jsonPath("$.length").value(greaterThan(0)) + }; return getEndpoints().stream() .filter(e -> e.startsWith("/api") && !e.equals("/api/hello")) .map( url -> { - String requestUrl = url.replace("{id}", "2").replace("{index}", "2"); + String requestUrl = + url.replace("examples/{id}", "examples/2") + .replace("{chapterName}", "expression") + .replace("{mistakeId}", "2") + .replace("{exampleId}", "2"); return DynamicTest.dynamicTest( - url, () -> mockMvc.perform(get(requestUrl)).andExpectAll(matchers())); + url, () -> mockMvc.perform(get(requestUrl)).andExpectAll(matchers)); }); - - // return getEndpoints().stream() - // .filter(e -> e.startsWith("/api/questions")) - // .map( - // url -> - // DynamicTest.dynamicTest( - // url, () -> mockMvc.perform(get(url)).andExpectAll(matchers()))); } } diff --git a/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java b/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java index 79eb32f..4b81ee4 100644 --- a/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java +++ b/back/src/test/java/com/example/mistakes/expression/_01_OperationPriorityTests.java @@ -3,7 +3,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.example.mistakes.api.questions.QuestionEntity; import com.example.mistakes.service.QuestionService; import java.util.List; import java.util.stream.Stream; @@ -19,7 +18,7 @@ @RequiredArgsConstructor class _01_OperationPriorityTests { - @Autowired private QuestionService service; + @Autowired private QuestionService service; // need post-processing to remove redundant spaces // i.g. double-spaces or tab or newline to single space @@ -34,8 +33,8 @@ void testRegistration() { var classPath = "com/example/mistakes/expression/_01_OperationPriority.java"; var before = "int before(short lo, short hi) { return lo << 16 + hi; }"; var after = "int after(short lo, short hi) { return (lo << 16) + hi; }"; - var entity = service.findById("_01_1"); - assertEquals(entity.getId(), "_01_1"); + var entity = service.findOne("2_01_1"); + assertEquals(entity.getId(), "2_01_1"); assertEquals(entity.message(), message); assertEquals(entity.getPath().toString(), "src/main/java/" + classPath); @@ -58,10 +57,10 @@ void testRegistration2() { // System.out.println(entity.getBefore()); // System.out.println(entity.getAfter()); - var entity = service.findById("_01_2"); + var entity = service.findOne("2_01_2"); // var entity = service.find(2, 2); assertEquals(entity.message(), message); - assertEquals(entity.getId(), "_01_2"); + assertEquals(entity.getId(), "2_01_2"); assertEquals(entity.getPath().toString(), "src/main/java/" + classPath); assertTrue(normalize(entity.getBefore()).contains(before)); From 65a74456951bbc9348b40cf2f225efd1db4b8b99 Mon Sep 17 00:00:00 2001 From: Hyeon-hak Kim Date: Fri, 21 Mar 2025 03:21:45 +0900 Subject: [PATCH 22/22] =?UTF-8?q?=E2=9C=A8=20Feat=20:=20front=20fetch=20ex?= =?UTF-8?q?amples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/questions/ChaptersController.java | 2 +- .../api/questions/QuestionEntity.java | 17 ++--- front/src/components/Navigator.astro | 26 +------ front/src/modules/fetchData.ts | 8 ++- front/src/modules/getEndpoints.ts | 70 ++++++++++++------- .../[mistakeId]/[exampleId].astro} | 15 ++-- 6 files changed, 69 insertions(+), 69 deletions(-) rename front/src/pages/{[subject]/[id].astro => [chapterName]/[mistakeId]/[exampleId].astro} (54%) diff --git a/back/src/main/java/com/example/mistakes/api/questions/ChaptersController.java b/back/src/main/java/com/example/mistakes/api/questions/ChaptersController.java index 5ee9124..e798710 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/ChaptersController.java +++ b/back/src/main/java/com/example/mistakes/api/questions/ChaptersController.java @@ -34,7 +34,7 @@ public ResponseEntity> getAllMistakesInChapter( @GetMapping("/{chapterName}/{mistakeId}") public ResponseEntity> getExamplesInMistake( @PathVariable String chapterName, @PathVariable Integer mistakeId) { - final var data = this.service.findAllByChapterName(chapterName); + final var data = this.service.findAllByMistakeId(mistakeId); return _response(data); } diff --git a/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java b/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java index b4b8db5..3125eaa 100644 --- a/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java +++ b/back/src/main/java/com/example/mistakes/api/questions/QuestionEntity.java @@ -11,6 +11,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.extern.log4j.Log4j2; @@ -25,25 +26,17 @@ final class ResClassQuestion extends ResponseManyDTO { @Log4j2 public record QuestionEntity(String message) implements Message, Identifiable, FsMeta { - public Number getChapter() { - var data = new HashMap(); - data.put("expression", 2); + private static Map chapterMap = new HashMap<>(Map.of("expression", 2)); + public Integer getChapter() { var topLevelPackage = message.split("\\.")[3]; - - return data.getOrDefault(topLevelPackage, 0); + return chapterMap.getOrDefault(topLevelPackage, 0); } public String getId() { - // TODO: refactor using getChapter() - var data = new HashMap(); - data.put("expression", 2); var topLevelPackage = message.split("\\.")[3]; - var chapter = data.getOrDefault(topLevelPackage, 0); + var chapter = chapterMap.getOrDefault(topLevelPackage, 0); - // new: return 1-2 when got _01_OperationPriority - // when getting `com.example.mistakes.expression._01_OperationPriority.Ex2` - // return id = "_01_2" Pattern pattern = Pattern.compile("_(\\d+)_\\S+(\\d+)"); Matcher matcher = pattern.matcher(message); if (matcher.find()) { diff --git a/front/src/components/Navigator.astro b/front/src/components/Navigator.astro index fc86c76..70b9751 100644 --- a/front/src/components/Navigator.astro +++ b/front/src/components/Navigator.astro @@ -1,23 +1,6 @@ --- -import { endpoints } from "../modules/getEndpoints"; +import { urlList } from "../modules/getEndpoints"; import Anchor from "./core/Anchor.astro"; - -// 숫자로 끝나는 주제별 첫 페이지만 필터링 & 중복 제거 -const uniqueSubjects = new Set(); -const filteredEndpoints = endpoints.filter((endpoint) => { - // 숫자가 아닌 ID는 항상 포함 (예: 't3') - if (isNaN(Number(endpoint.id))) { - return true; - } - - // 숫자인 경우 각 주제의 첫 번째 페이지만 포함 - if (!uniqueSubjects.has(endpoint.subject) && endpoint.id === "1") { - uniqueSubjects.add(endpoint.subject); - return true; - } - - return false; -}); ---