From b4bcd5a422bcd3b527629c7f4ac9d0474ee73145 Mon Sep 17 00:00:00 2001 From: haokingluo Date: Fri, 17 Apr 2026 14:41:16 -0400 Subject: [PATCH 1/2] 872: Migrate BackgroundTaskRepo to use Optional Modified two methods inside BackgroundTaskRepository.java to use Java Optional, returning Optional.of() if non-null and replacing null returns with Optional.empty(), and updated call sites in other classes and test files, to avoid possibility of null pointer exceptions and null checks. --- .../repos/task/BackgroundTaskRepository.java | 5 +- .../task/BackgroundTaskSqlRepository.java | 14 ++--- .../leetcode/FetchAllLeetcodeQuestions.java | 11 ++-- .../metrics/AddUserMetricsService.java | 9 ++-- .../task/BackgroundTaskRepositoryTest.java | 52 ++++++++++--------- .../FetchAllLeetcodeQuestionsTest.java | 3 +- .../metrics/AddUserMetricsServiceTest.java | 4 +- 7 files changed, 54 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/patinanetwork/codebloom/common/db/repos/task/BackgroundTaskRepository.java b/src/main/java/org/patinanetwork/codebloom/common/db/repos/task/BackgroundTaskRepository.java index c75682131..5d08a0741 100644 --- a/src/main/java/org/patinanetwork/codebloom/common/db/repos/task/BackgroundTaskRepository.java +++ b/src/main/java/org/patinanetwork/codebloom/common/db/repos/task/BackgroundTaskRepository.java @@ -1,6 +1,7 @@ package org.patinanetwork.codebloom.common.db.repos.task; import java.util.List; +import java.util.Optional; import org.patinanetwork.codebloom.common.db.models.task.BackgroundTask; import org.patinanetwork.codebloom.common.db.models.task.BackgroundTaskEnum; @@ -18,11 +19,11 @@ public interface BackgroundTaskRepository { */ void createBackgroundTask(BackgroundTask task); - BackgroundTask getBackgroundTaskById(String id); + Optional getBackgroundTaskById(String id); List getBackgroundTasksByTaskEnum(BackgroundTaskEnum taskEnum); - BackgroundTask getMostRecentlyCompletedBackgroundTaskByTaskEnum(BackgroundTaskEnum taskEnum); + Optional getMostRecentlyCompletedBackgroundTaskByTaskEnum(BackgroundTaskEnum taskEnum); /** * @note - The provided object's methods will be overridden with any returned data from the database. diff --git a/src/main/java/org/patinanetwork/codebloom/common/db/repos/task/BackgroundTaskSqlRepository.java b/src/main/java/org/patinanetwork/codebloom/common/db/repos/task/BackgroundTaskSqlRepository.java index 889c5be7d..7b61da096 100644 --- a/src/main/java/org/patinanetwork/codebloom/common/db/repos/task/BackgroundTaskSqlRepository.java +++ b/src/main/java/org/patinanetwork/codebloom/common/db/repos/task/BackgroundTaskSqlRepository.java @@ -6,6 +6,7 @@ import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.UUID; import javax.sql.DataSource; import org.patinanetwork.codebloom.common.db.helper.NamedPreparedStatement; @@ -61,7 +62,7 @@ public void createBackgroundTask(final BackgroundTask task) { } @Override - public BackgroundTask getBackgroundTaskById(final String id) { + public Optional getBackgroundTaskById(final String id) { String sql = """ SELECT * @@ -76,14 +77,14 @@ public BackgroundTask getBackgroundTaskById(final String id) { stmt.setObject("id", UUID.fromString(id)); try (ResultSet rs = stmt.executeQuery()) { if (rs.next()) { - return parseResultSetToBackgroundTask(rs); + return Optional.of(parseResultSetToBackgroundTask(rs)); } } } catch (Exception e) { throw new RuntimeException("Failed to get background task by ID", e); } - return null; + return Optional.empty(); } @Override @@ -114,7 +115,8 @@ public List getBackgroundTasksByTaskEnum(final BackgroundTaskEnu } @Override - public BackgroundTask getMostRecentlyCompletedBackgroundTaskByTaskEnum(final BackgroundTaskEnum taskEnum) { + public Optional getMostRecentlyCompletedBackgroundTaskByTaskEnum( + final BackgroundTaskEnum taskEnum) { String sql = """ SELECT * @@ -132,14 +134,14 @@ public BackgroundTask getMostRecentlyCompletedBackgroundTaskByTaskEnum(final Bac stmt.setObject("task", taskEnum.name(), java.sql.Types.OTHER); try (ResultSet rs = stmt.executeQuery()) { if (rs.next()) { - return parseResultSetToBackgroundTask(rs); + return Optional.of(parseResultSetToBackgroundTask(rs)); } } } catch (Exception e) { throw new RuntimeException("Failed to get most recently completed background task by task enum", e); } - return null; + return Optional.empty(); } @Override diff --git a/src/main/java/org/patinanetwork/codebloom/scheduled/leetcode/FetchAllLeetcodeQuestions.java b/src/main/java/org/patinanetwork/codebloom/scheduled/leetcode/FetchAllLeetcodeQuestions.java index dc4d605d1..a893e27f2 100644 --- a/src/main/java/org/patinanetwork/codebloom/scheduled/leetcode/FetchAllLeetcodeQuestions.java +++ b/src/main/java/org/patinanetwork/codebloom/scheduled/leetcode/FetchAllLeetcodeQuestions.java @@ -4,6 +4,7 @@ import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -59,11 +60,13 @@ private void runStartupSynchronization() { @Scheduled(initialDelay = 1, fixedDelay = 3, timeUnit = TimeUnit.HOURS) public void updateQuestionBank() { - BackgroundTask recentLeetcodeTask = backgroundTaskRepository.getMostRecentlyCompletedBackgroundTaskByTaskEnum( - BackgroundTaskEnum.LEETCODE_QUESTION_BANK); - if (recentLeetcodeTask != null) { + Optional recentLeetcodeTask = + backgroundTaskRepository.getMostRecentlyCompletedBackgroundTaskByTaskEnum( + BackgroundTaskEnum.LEETCODE_QUESTION_BANK); + + if (recentLeetcodeTask.isPresent()) { if (StandardizedOffsetDateTime.now() - .isBefore(recentLeetcodeTask.getCompletedAt().plusHours(16))) { + .isBefore(recentLeetcodeTask.get().getCompletedAt().plusHours(16))) { log.error("Not time yet to resync question bank"); return; } diff --git a/src/main/java/org/patinanetwork/codebloom/scheduled/metrics/AddUserMetricsService.java b/src/main/java/org/patinanetwork/codebloom/scheduled/metrics/AddUserMetricsService.java index d75cc0ed9..19c46c0ac 100644 --- a/src/main/java/org/patinanetwork/codebloom/scheduled/metrics/AddUserMetricsService.java +++ b/src/main/java/org/patinanetwork/codebloom/scheduled/metrics/AddUserMetricsService.java @@ -2,6 +2,7 @@ import java.time.ZoneOffset; import java.util.List; +import java.util.Optional; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; import org.patinanetwork.codebloom.common.components.LeaderboardException; @@ -40,7 +41,7 @@ public AddUserMetricsService( @Scheduled(initialDelay = 0, fixedDelay = 30, timeUnit = TimeUnit.MINUTES) public void congregateUserMetrics() { try { - BackgroundTask recentMetricsTask = + Optional recentMetricsTask = backgroundTaskRepository.getMostRecentlyCompletedBackgroundTaskByTaskEnum( BackgroundTaskEnum.USER_METRICS); @@ -50,9 +51,9 @@ public void congregateUserMetrics() { .atStartOfDay(ZoneOffset.UTC) .toOffsetDateTime(); - if (recentMetricsTask != null - && recentMetricsTask.getCompletedAt() != null - && !recentMetricsTask.getCompletedAt().isBefore(midnight)) { + if (recentMetricsTask.isPresent() + && recentMetricsTask.get().getCompletedAt() != null + && !recentMetricsTask.get().getCompletedAt().isBefore(midnight)) { log.info("Skipping user metrics sync because today's snapshot already exists."); return; } diff --git a/src/test/java/org/patinanetwork/codebloom/common/db/repos/task/BackgroundTaskRepositoryTest.java b/src/test/java/org/patinanetwork/codebloom/common/db/repos/task/BackgroundTaskRepositoryTest.java index 3673377d8..29e3087b2 100644 --- a/src/test/java/org/patinanetwork/codebloom/common/db/repos/task/BackgroundTaskRepositoryTest.java +++ b/src/test/java/org/patinanetwork/codebloom/common/db/repos/task/BackgroundTaskRepositoryTest.java @@ -4,6 +4,7 @@ import java.time.OffsetDateTime; import java.util.List; +import java.util.Optional; import java.util.UUID; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.*; @@ -54,9 +55,9 @@ void cleanUp() { @Test @Order(1) void testGetBackgroundTaskById() { - BackgroundTask found = backgroundTaskRepository.getBackgroundTaskById(testTask.getId()); - assertNotNull(found); - assertEquals(testTask, found); + Optional found = backgroundTaskRepository.getBackgroundTaskById(testTask.getId()); + assertTrue(found.isPresent()); + assertEquals(testTask, found.get()); } @Test @@ -73,11 +74,11 @@ void testGetBackgroundTasksByTaskEnum() { @Test @Order(3) void testGetMostRecentlyCompletedBackgroundTaskByTaskEnum() { - BackgroundTask recentTask = backgroundTaskRepository.getMostRecentlyCompletedBackgroundTaskByTaskEnum( + Optional recentTask = backgroundTaskRepository.getMostRecentlyCompletedBackgroundTaskByTaskEnum( BackgroundTaskEnum.LEETCODE_QUESTION_BANK); - assertNotNull(recentTask); - assertEquals(BackgroundTaskEnum.LEETCODE_QUESTION_BANK, recentTask.getTask()); - assertNotNull(recentTask.getCompletedAt()); + assertTrue(recentTask.isPresent()); + assertEquals(BackgroundTaskEnum.LEETCODE_QUESTION_BANK, recentTask.get().getTask()); + assertNotNull(recentTask.get().getCompletedAt()); } @Test @@ -93,10 +94,10 @@ void testUpdateBackgroundTaskById() { boolean updateResult = backgroundTaskRepository.updateBackgroundTaskById(updatedTask); assertTrue(updateResult); - BackgroundTask retrieved = backgroundTaskRepository.getBackgroundTaskById(testTask.getId()); - assertNotNull(retrieved); - assertEquals(BackgroundTaskEnum.LEETCODE_QUESTION_BANK, retrieved.getTask()); - assertEquals(newCompletedAt, retrieved.getCompletedAt()); + Optional retrieved = backgroundTaskRepository.getBackgroundTaskById(testTask.getId()); + assertTrue(retrieved.isPresent()); + assertEquals(BackgroundTaskEnum.LEETCODE_QUESTION_BANK, retrieved.get().getTask()); + assertEquals(newCompletedAt, retrieved.get().getCompletedAt()); } @Test @@ -110,18 +111,18 @@ void testCreateAnotherBackgroundTask() { backgroundTaskRepository.createBackgroundTask(anotherTask); assertNotNull(anotherTask.getId()); - BackgroundTask found = backgroundTaskRepository.getBackgroundTaskById(anotherTask.getId()); - assertNotNull(found); - assertEquals(anotherTask.getTask(), found.getTask()); - assertEquals(anotherTask, found); + Optional found = backgroundTaskRepository.getBackgroundTaskById(anotherTask.getId()); + assertTrue(found.isPresent()); + assertEquals(anotherTask.getTask(), found.get().getTask()); + assertEquals(anotherTask, found.get()); } @Test @Order(6) void testGetBackgroundTaskByIdNotFound() { - BackgroundTask notFound = + Optional notFound = backgroundTaskRepository.getBackgroundTaskById(UUID.randomUUID().toString()); - assertNull(notFound); + assertTrue(notFound.isEmpty()); } @Test @@ -148,9 +149,10 @@ void testCreateBackgroundTaskWithNullCompletedAt() { backgroundTaskRepository.createBackgroundTask(taskWithNullCompletedAt); assertNotNull(taskWithNullCompletedAt.getId()); - BackgroundTask retrieved = backgroundTaskRepository.getBackgroundTaskById(taskWithNullCompletedAt.getId()); - assertNotNull(retrieved); - assertNotNull(retrieved.getCompletedAt(), "completedAt should be set to current time when null"); + Optional retrieved = + backgroundTaskRepository.getBackgroundTaskById(taskWithNullCompletedAt.getId()); + assertTrue(retrieved.isPresent()); + assertNotNull(retrieved.get().getCompletedAt(), "completedAt should be set to current time when null"); } @Test @@ -224,15 +226,15 @@ void testDeleteBackgroundTaskById() { backgroundTaskRepository.createBackgroundTask(deletableTask); assertNotNull(deletableTask.getId()); - BackgroundTask found = backgroundTaskRepository.getBackgroundTaskById(deletableTask.getId()); - assertNotNull(found); - assertEquals(deletableTask, found); + Optional found = backgroundTaskRepository.getBackgroundTaskById(deletableTask.getId()); + assertTrue(found.isPresent()); + assertEquals(deletableTask, found.get()); boolean deleted = backgroundTaskRepository.deleteBackgroundTaskById(deletableTask.getId()); assertTrue(deleted); - BackgroundTask deletedTask = backgroundTaskRepository.getBackgroundTaskById(deletableTask.getId()); - assertNull(deletedTask); + Optional deletedTask = backgroundTaskRepository.getBackgroundTaskById(deletableTask.getId()); + assertTrue(deletedTask.isEmpty()); } @Test diff --git a/src/test/java/org/patinanetwork/codebloom/scheduled/leetcode/FetchAllLeetcodeQuestionsTest.java b/src/test/java/org/patinanetwork/codebloom/scheduled/leetcode/FetchAllLeetcodeQuestionsTest.java index 4c95a6f6e..19c85a5fa 100644 --- a/src/test/java/org/patinanetwork/codebloom/scheduled/leetcode/FetchAllLeetcodeQuestionsTest.java +++ b/src/test/java/org/patinanetwork/codebloom/scheduled/leetcode/FetchAllLeetcodeQuestionsTest.java @@ -9,6 +9,7 @@ import java.time.OffsetDateTime; import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -46,7 +47,7 @@ void setup() { when(backgroundTaskRepository.getMostRecentlyCompletedBackgroundTaskByTaskEnum( BackgroundTaskEnum.LEETCODE_QUESTION_BANK)) - .thenReturn(lastSync); + .thenReturn(Optional.of(lastSync)); } @Test diff --git a/src/test/java/org/patinanetwork/codebloom/scheduled/metrics/AddUserMetricsServiceTest.java b/src/test/java/org/patinanetwork/codebloom/scheduled/metrics/AddUserMetricsServiceTest.java index f1fda9115..0660d168b 100644 --- a/src/test/java/org/patinanetwork/codebloom/scheduled/metrics/AddUserMetricsServiceTest.java +++ b/src/test/java/org/patinanetwork/codebloom/scheduled/metrics/AddUserMetricsServiceTest.java @@ -49,7 +49,7 @@ void congregateUserMetricsCreatesSnapshotsForCurrentLeaderboard() throws Leaderb .completedAt(OffsetDateTime.now().minusDays(2)) .build(); when(backgroundTaskRepository.getMostRecentlyCompletedBackgroundTaskByTaskEnum(BackgroundTaskEnum.USER_METRICS)) - .thenReturn(lastSync); + .thenReturn(Optional.of(lastSync)); when(leaderboardRepository.getRecentLeaderboardMetadata()) .thenReturn(Optional.of(Leaderboard.builder() @@ -97,7 +97,7 @@ void congregateUserMetricsSkipsWhenTodayEstSnapshotAlreadyExists() throws Leader .completedAt(completedAfterTodayEstMidnight) .build(); when(backgroundTaskRepository.getMostRecentlyCompletedBackgroundTaskByTaskEnum(BackgroundTaskEnum.USER_METRICS)) - .thenReturn(lastSync); + .thenReturn(Optional.of(lastSync)); service.congregateUserMetrics(); From 634e974d9b5f6a00a0819d7510fdac0c08e7f783 Mon Sep 17 00:00:00 2001 From: haokingluo Date: Wed, 22 Apr 2026 16:07:14 -0400 Subject: [PATCH 2/2] 872: code coverage Modified tests to reach sonarqube code coverage --- .../task/BackgroundTaskRepositoryTest.java | 4 ++ .../FetchAllLeetcodeQuestionsTest.java | 51 +++++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/patinanetwork/codebloom/common/db/repos/task/BackgroundTaskRepositoryTest.java b/src/test/java/org/patinanetwork/codebloom/common/db/repos/task/BackgroundTaskRepositoryTest.java index 29e3087b2..58dbb044d 100644 --- a/src/test/java/org/patinanetwork/codebloom/common/db/repos/task/BackgroundTaskRepositoryTest.java +++ b/src/test/java/org/patinanetwork/codebloom/common/db/repos/task/BackgroundTaskRepositoryTest.java @@ -32,6 +32,10 @@ public BackgroundTaskRepositoryTest(final BackgroundTaskSqlRepository background @BeforeAll void createBackgroundTask() { + Optional recentTask = backgroundTaskRepository.getMostRecentlyCompletedBackgroundTaskByTaskEnum( + BackgroundTaskEnum.LEETCODE_QUESTION_BANK); + assertTrue(recentTask.isEmpty()); + testTask = BackgroundTask.builder() .task(BackgroundTaskEnum.LEETCODE_QUESTION_BANK) .completedAt(StandardizedOffsetDateTime.now()) diff --git a/src/test/java/org/patinanetwork/codebloom/scheduled/leetcode/FetchAllLeetcodeQuestionsTest.java b/src/test/java/org/patinanetwork/codebloom/scheduled/leetcode/FetchAllLeetcodeQuestionsTest.java index 19c85a5fa..3b75e2527 100644 --- a/src/test/java/org/patinanetwork/codebloom/scheduled/leetcode/FetchAllLeetcodeQuestionsTest.java +++ b/src/test/java/org/patinanetwork/codebloom/scheduled/leetcode/FetchAllLeetcodeQuestionsTest.java @@ -1,6 +1,7 @@ package org.patinanetwork.codebloom.scheduled.leetcode; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -70,17 +71,31 @@ void testSyncCreatesAndDeletesQuestions() { .acceptanceRate(33.0f) .build(); - when(leetcodeClient.getAllProblems()).thenReturn(List.of(q1, q2)); + LeetcodeQuestion q3 = LeetcodeQuestion.builder() + .titleSlug("climbing-stairs") + .questionTitle("Climbing Stairs") + .questionId(3) + .difficulty("Hard") + .link("l3") + .acceptanceRate(12.5f) + .build(); + + when(leetcodeClient.getAllProblems()).thenReturn(List.of(q1, q2, q3)); - QuestionBank existing = + QuestionBank existing1 = QuestionBank.builder().id("id-existing").questionSlug("two-sum").build(); + QuestionBank existing2 = QuestionBank.builder() + .id("id-existing-2") + .questionSlug("climbing-stairs") + .build(); + QuestionBank outdated = QuestionBank.builder() .id("id-outdated") .questionSlug("old-question") .build(); - when(questionBankRepository.getAllQuestions()).thenReturn(List.of(existing, outdated)); + when(questionBankRepository.getAllQuestions()).thenReturn(List.of(existing1, existing2, outdated)); job.updateQuestionBank(); @@ -89,13 +104,41 @@ void testSyncCreatesAndDeletesQuestions() { assertEquals("add-two-numbers", createCaptor.getValue().getQuestionSlug()); verify(questionBankRepository, times(1)).deleteQuestionById("id-outdated"); - ArgumentCaptor taskCaptor = ArgumentCaptor.forClass(BackgroundTask.class); verify(backgroundTaskRepository, times(1)).createBackgroundTask(taskCaptor.capture()); assertEquals( BackgroundTaskEnum.LEETCODE_QUESTION_BANK, taskCaptor.getValue().getTask()); } + @Test + void testUpdateQuestionBankInvalidDifficulty() { + LeetcodeQuestion q1 = LeetcodeQuestion.builder() + .titleSlug("climbing-stairs") + .questionTitle("Climbing Stairs") + .questionId(1) + .difficulty("Very Difficult") + .link("l1") + .acceptanceRate(50.0f) + .build(); + + when(leetcodeClient.getAllProblems()).thenReturn(List.of(q1)); + QuestionBank existing = QuestionBank.builder() + .id("id-existing") + .questionSlug("climbing-stairs") + .build(); + QuestionBank outdated = QuestionBank.builder() + .id("id-outdated") + .questionSlug("old-question") + .build(); + when(questionBankRepository.getAllQuestions()).thenReturn(List.of(existing, outdated)); + assertThrows( + IllegalArgumentException.class, + () -> { + job.updateQuestionBank(); + }, + "Updating with invalid leetcode question difficulty should throw exception"); + } + @Test void testInit() { when(env.isDev()).thenReturn(true);