From 6a8a0cad902b29030a1170a3ff0d33a32bed3fc4 Mon Sep 17 00:00:00 2001 From: Arshadul Monir Date: Mon, 2 Mar 2026 22:14:29 -0500 Subject: [PATCH 1/3] 817: Replaced API call with QuestionBank query --- .../submissions/SubmissionsHandler.java | 63 ++++++++++++------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/patinanetwork/codebloom/common/submissions/SubmissionsHandler.java b/src/main/java/org/patinanetwork/codebloom/common/submissions/SubmissionsHandler.java index e011fdd49..60047b10d 100644 --- a/src/main/java/org/patinanetwork/codebloom/common/submissions/SubmissionsHandler.java +++ b/src/main/java/org/patinanetwork/codebloom/common/submissions/SubmissionsHandler.java @@ -17,6 +17,7 @@ import org.patinanetwork.codebloom.common.db.models.potd.POTD; import org.patinanetwork.codebloom.common.db.models.question.Question; import org.patinanetwork.codebloom.common.db.models.question.QuestionDifficulty; +import org.patinanetwork.codebloom.common.db.models.question.bank.QuestionBank; import org.patinanetwork.codebloom.common.db.models.question.topic.LeetcodeTopicEnum; import org.patinanetwork.codebloom.common.db.models.question.topic.QuestionTopic; import org.patinanetwork.codebloom.common.db.models.user.User; @@ -25,6 +26,7 @@ import org.patinanetwork.codebloom.common.db.repos.leaderboard.LeaderboardRepository; import org.patinanetwork.codebloom.common.db.repos.potd.POTDRepository; import org.patinanetwork.codebloom.common.db.repos.question.QuestionRepository; +import org.patinanetwork.codebloom.common.db.repos.question.questionbank.QuestionBankRepository; import org.patinanetwork.codebloom.common.db.repos.question.topic.QuestionTopicRepository; import org.patinanetwork.codebloom.common.db.repos.user.UserRepository; import org.patinanetwork.codebloom.common.db.repos.user.options.UserFilterOptions; @@ -53,6 +55,7 @@ public class SubmissionsHandler { private final QuestionTopicRepository questionTopicRepository; private final JobRepository jobRepository; private final Reporter throttledReporter; + private final QuestionBankRepository questionBankRepository; private boolean isValid(final LocalDateTime createdAt) { // TODO - Replace EST locked functionality. @@ -76,7 +79,8 @@ public SubmissionsHandler( final UserRepository userRepository, final QuestionTopicRepository questionTopicRepository, final ThrottledReporter throttledReporter, - final JobRepository jobRepository) { + final JobRepository jobRepository, + final QuestionBankRepository questionBankRepository) { this.questionRepository = questionRepository; this.leetcodeClient = throttledLeetcodeClient; this.leaderboardRepository = leaderboardRepository; @@ -85,6 +89,7 @@ public SubmissionsHandler( this.questionTopicRepository = questionTopicRepository; this.jobRepository = jobRepository; this.throttledReporter = throttledReporter; + this.questionBankRepository = questionBankRepository; } public static Predicate distinctByKey(final Function keyExtractor) { @@ -99,11 +104,29 @@ public ArrayList handleSubmissions( var questionMap = leetcodeSubmissions.parallelStream() .filter(distinctByKey(LeetcodeSubmission::getTitleSlug)) - .map(s -> Pair.of( - s.getTitleSlug(), - fast - ? leetcodeClient.findQuestionBySlugFast(s.getTitleSlug()) - : leetcodeClient.findQuestionBySlug(s.getTitleSlug()))) + .map(s -> { + String slug = s.getTitleSlug(); + + QuestionBank bankQuestion = questionBankRepository.getQuestionBySlug(slug); + + if (bankQuestion == null) { + LeetcodeQuestion question = fast + ? leetcodeClient.findQuestionBySlugFast(slug) + : leetcodeClient.findQuestionBySlug(slug); + + bankQuestion = QuestionBank.builder() + .questionSlug(question.getTitleSlug()) + .questionDifficulty(QuestionDifficulty.valueOf(question.getDifficulty())) + .questionTitle(question.getQuestionTitle()) + .questionNumber(question.getQuestionId()) + .questionLink("https://leetcode.com/problems/" + question.getTitleSlug()) + .description(question.getQuestion()) + .acceptanceRate(question.getAcceptanceRate()) + .build(); + } + + return Pair.of(slug, bankQuestion); + }) .collect(Collectors.toMap(p -> p.getLeft(), p -> p.getRight())); for (LeetcodeSubmission leetcodeSubmission : leetcodeSubmissions) { @@ -127,7 +150,7 @@ public ArrayList handleSubmissions( multiplier = potd.getMultiplier(); } - LeetcodeQuestion leetcodeQuestion = questionMap.get(leetcodeSubmission.getTitleSlug()); + QuestionBank bankQuestion = questionMap.get(leetcodeSubmission.getTitleSlug()); // If the submission is before the leaderboard started, points awarded = 0 Leaderboard recentLeaderboard = leaderboardRepository.getRecentLeaderboardMetadata(); @@ -152,9 +175,7 @@ public ArrayList handleSubmissions( points = 0; } else { points = ScoreCalculator.calculateScore( - QuestionDifficulty.valueOf(leetcodeQuestion.getDifficulty()), - leetcodeQuestion.getAcceptanceRate(), - multiplier); + bankQuestion.getQuestionDifficulty(), bankQuestion.getAcceptanceRate(), multiplier); } // throttledReporter.log(Report.builder() @@ -178,14 +199,14 @@ public ArrayList handleSubmissions( Question newQuestion = Question.builder() .userId(user.getId()) - .questionSlug(leetcodeQuestion.getTitleSlug()) - .questionDifficulty(QuestionDifficulty.valueOf(leetcodeQuestion.getDifficulty())) - .questionNumber(leetcodeQuestion.getQuestionId()) - .questionLink("https://leetcode.com/problems/" + leetcodeQuestion.getTitleSlug()) - .questionTitle(leetcodeQuestion.getQuestionTitle()) - .description(leetcodeQuestion.getQuestion()) + .questionSlug(bankQuestion.getQuestionSlug()) + .questionDifficulty(bankQuestion.getQuestionDifficulty()) + .questionNumber(bankQuestion.getQuestionNumber()) + .questionLink("https://leetcode.com/problems/" + bankQuestion.getQuestionSlug()) + .questionTitle(bankQuestion.getQuestionTitle()) + .description(bankQuestion.getDescription()) .pointsAwarded(points) - .acceptanceRate(leetcodeQuestion.getAcceptanceRate()) + .acceptanceRate(bankQuestion.getAcceptanceRate()) .submittedAt(leetcodeSubmission.getTimestamp()) .submissionId(String.valueOf(leetcodeSubmission.getId())) .build(); @@ -199,15 +220,15 @@ public ArrayList handleSubmissions( jobRepository.createJob(newJob); - leetcodeQuestion.getTopics().stream() + bankQuestion.getTopics().stream() .forEach(topic -> questionTopicRepository.createQuestionTopic(QuestionTopic.builder() .questionId(newQuestion.getId()) - .topicSlug(topic.getSlug()) - .topic(LeetcodeTopicEnum.fromValue(topic.getSlug())) + .topicSlug(topic.getTopicSlug()) + .topic(LeetcodeTopicEnum.fromValue(topic.getTopicSlug())) .build())); acceptedSubmissions.add( - new AcceptedSubmission(leetcodeQuestion.getQuestionTitle(), createdQuestion.getId(), points)); + new AcceptedSubmission(bankQuestion.getQuestionTitle(), createdQuestion.getId(), points)); UserWithScore recentUserMetadata = userRepository.getUserWithScoreByIdAndLeaderboardId( user.getId(), recentLeaderboard.getId(), UserFilterOptions.DEFAULT); From 989ea60f38e45c1033ead75183f86715d23b8ebd Mon Sep 17 00:00:00 2001 From: Arshadul Monir Date: Tue, 3 Mar 2026 15:52:08 -0500 Subject: [PATCH 2/3] 817: Added test for SubmissionHandler --- .../submissions/SubmissionsHandler.java | 4 +- .../submissions/SubmissionHandlerTest.java | 151 ++++++++++++++++++ 2 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/patinanetwork/codebloom/common/submissions/SubmissionHandlerTest.java diff --git a/src/main/java/org/patinanetwork/codebloom/common/submissions/SubmissionsHandler.java b/src/main/java/org/patinanetwork/codebloom/common/submissions/SubmissionsHandler.java index 60047b10d..002cd8cc9 100644 --- a/src/main/java/org/patinanetwork/codebloom/common/submissions/SubmissionsHandler.java +++ b/src/main/java/org/patinanetwork/codebloom/common/submissions/SubmissionsHandler.java @@ -211,8 +211,6 @@ public ArrayList handleSubmissions( .submissionId(String.valueOf(leetcodeSubmission.getId())) .build(); - var createdQuestion = questionRepository.createQuestion(newQuestion); - Job newJob = Job.builder() .questionId(newQuestion.getId()) .status(JobStatus.INCOMPLETE) @@ -228,7 +226,7 @@ public ArrayList handleSubmissions( .build())); acceptedSubmissions.add( - new AcceptedSubmission(bankQuestion.getQuestionTitle(), createdQuestion.getId(), points)); + new AcceptedSubmission(bankQuestion.getQuestionTitle(), newQuestion.getId(), points)); UserWithScore recentUserMetadata = userRepository.getUserWithScoreByIdAndLeaderboardId( user.getId(), recentLeaderboard.getId(), UserFilterOptions.DEFAULT); diff --git a/src/test/java/org/patinanetwork/codebloom/common/submissions/SubmissionHandlerTest.java b/src/test/java/org/patinanetwork/codebloom/common/submissions/SubmissionHandlerTest.java new file mode 100644 index 000000000..a50d171ac --- /dev/null +++ b/src/test/java/org/patinanetwork/codebloom/common/submissions/SubmissionHandlerTest.java @@ -0,0 +1,151 @@ +package org.patinanetwork.codebloom.common.submissions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.patinanetwork.codebloom.common.db.models.leaderboard.Leaderboard; +import org.patinanetwork.codebloom.common.db.models.question.QuestionDifficulty; +import org.patinanetwork.codebloom.common.db.models.question.bank.QuestionBank; +import org.patinanetwork.codebloom.common.db.models.user.User; +import org.patinanetwork.codebloom.common.db.models.user.UserWithScore; +import org.patinanetwork.codebloom.common.db.repos.job.JobRepository; +import org.patinanetwork.codebloom.common.db.repos.leaderboard.LeaderboardRepository; +import org.patinanetwork.codebloom.common.db.repos.potd.POTDRepository; +import org.patinanetwork.codebloom.common.db.repos.question.QuestionRepository; +import org.patinanetwork.codebloom.common.db.repos.question.questionbank.QuestionBankRepository; +import org.patinanetwork.codebloom.common.db.repos.question.topic.QuestionTopicRepository; +import org.patinanetwork.codebloom.common.db.repos.user.UserRepository; +import org.patinanetwork.codebloom.common.db.repos.user.options.UserFilterOptions; +import org.patinanetwork.codebloom.common.leetcode.models.LeetcodeSubmission; +import org.patinanetwork.codebloom.common.leetcode.throttled.ThrottledLeetcodeClient; +import org.patinanetwork.codebloom.common.reporter.throttled.ThrottledReporter; +import org.patinanetwork.codebloom.common.submissions.object.AcceptedSubmission; + +public class SubmissionHandlerTest { + private final QuestionRepository questionRepository = mock(QuestionRepository.class); + private final ThrottledLeetcodeClient leetcodeClient = mock(ThrottledLeetcodeClient.class); + private final LeaderboardRepository leaderboardRepository = mock(LeaderboardRepository.class); + private final POTDRepository potdRepository = mock(POTDRepository.class); + private final UserRepository userRepository = mock(UserRepository.class); + private final QuestionTopicRepository questionTopicRepository = mock(QuestionTopicRepository.class); + private final JobRepository jobRepository = mock(JobRepository.class); + private final ThrottledReporter throttledReporter = mock(ThrottledReporter.class); + private final QuestionBankRepository questionBankRepository = mock(QuestionBankRepository.class); + + private SubmissionsHandler submissionsHandler; + + @BeforeEach + void setup() { + submissionsHandler = new SubmissionsHandler( + questionRepository, + leetcodeClient, + leaderboardRepository, + potdRepository, + userRepository, + questionTopicRepository, + throttledReporter, + jobRepository, + questionBankRepository); + } + + // @Test + // void testHandleSubmission() { + // LeetcodeSubmission mockSub = mock(LeetcodeSubmission.class); + // List mockSubmissions = List.of(mockSub); + // User mockUser = mock(User.class); + // POTD mockPOTD = mock(POTD.class); + // QuestionBank mockBank = QuestionBank.builder() + // .id("bdbba382-1738-11f1-a2ff-4f7d0fe833a2") + // .questionSlug("hello-world") + // .questionDifficulty(QuestionDifficulty.Easy) + // .questionTitle("Hello World") + // .questionNumber(1) + // .questionLink("https://leetcode.com/problems/hello-world") + // .description("Print Hello World to the terminal") + // .acceptanceRate(1.0f) + // .createdAt(OffsetDateTime.now()) + // .topics(List.of()) + // .build(); + // Leaderboard mockLeaderboard = mock(Leaderboard.class); + + // when(potdRepository.getCurrentPOTD()).thenReturn(mockPOTD); + // when(mockSubmissions.parallelStream() + // .filter(SubmissionsHandler.distinctByKey(LeetcodeSubmission::getTitleSlug)) + // .map(any()) + // .collect(any())) + // .thenReturn(Map.of("mockSlug", mockBank)); + + // when(mockSub.getStatusDisplay()).thenReturn("Accepted"); + // when(questionRepository.questionExistsBySubmissionId(String.valueOf(mockSub.getId()))) + // .thenReturn(false); + // when(mockPOTD.getCreatedAt()).thenReturn(LocalDateTime.now()); + // when(anyMap().get(anyString())).thenReturn(mockBank); + // when(leaderboardRepository.getRecentLeaderboardMetadata()).thenReturn(mockLeaderboard); + // when(mockLeaderboard.getCreatedAt().isAfter(mockSub.getTimestamp())).thenReturn(false); + + // when(questionRepository.createQuestion(any(Question.class))).thenReturn(null); + // // when(bankQuestion.get) + // } + + @Test + void testHandleSubmissionSuccess() { + LeetcodeSubmission submission = + new LeetcodeSubmission(1, "Hello World", "hello-world", LocalDateTime.now(), "Accepted"); + + User user = mock(User.class); + when(user.getId()).thenReturn("fb632fce-173e-11f1-8350-6f6b49d02c1c"); + + when(potdRepository.getCurrentPOTD()).thenReturn(null); + + when(questionRepository.questionExistsBySubmissionId("1")).thenReturn(false); + + when(questionRepository.getQuestionBySlugAndUserId("hello-world", "fb632fce-173e-11f1-8350-6f6b49d02c1c")) + .thenReturn(null); + + Leaderboard leaderboard = mock(Leaderboard.class); + when(leaderboard.getId()).thenReturn("7c572df6-173f-11f1-8331-d3ac52e05366"); + when(leaderboard.getCreatedAt()).thenReturn(submission.getTimestamp().minusHours(1)); + when(leaderboardRepository.getRecentLeaderboardMetadata()).thenReturn(leaderboard); + + UserWithScore userWithScore = mock(UserWithScore.class); + when(userWithScore.getTotalScore()).thenReturn(100); + when(userRepository.getUserWithScoreByIdAndLeaderboardId( + "fb632fce-173e-11f1-8350-6f6b49d02c1c", + "7c572df6-173f-11f1-8331-d3ac52e05366", + UserFilterOptions.DEFAULT)) + .thenReturn(userWithScore); + + QuestionBank bankQuestion = QuestionBank.builder() + .questionSlug("hello-world") + .questionDifficulty(QuestionDifficulty.Easy) + .questionTitle("Hello World") + .questionNumber(1) + .questionLink("https://leetcode.com/problems/hello-world") + .description("Print Hello World") + .acceptanceRate(1.0f) + .topics(List.of()) + .build(); + + when(questionBankRepository.getQuestionBySlug("hello-world")).thenReturn(bankQuestion); + + List result = submissionsHandler.handleSubmissions(List.of(submission), user, true); + + assertEquals(1, result.size()); + + verify(jobRepository).createJob(any()); + verify(leaderboardRepository) + .updateUserPointsFromLeaderboard( + eq("7c572df6-173f-11f1-8331-d3ac52e05366"), + eq("fb632fce-173e-11f1-8350-6f6b49d02c1c"), + anyInt()); + } +} From d309b79a9b45dd70183faac1b34855bb2f3bb7bb Mon Sep 17 00:00:00 2001 From: Arshadul Monir Date: Wed, 4 Mar 2026 00:37:11 -0500 Subject: [PATCH 3/3] 817: Altered test to cover more code lines --- .../submissions/SubmissionsHandler.java | 6 +++++ .../submissions/SubmissionHandlerTest.java | 23 +++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/patinanetwork/codebloom/common/submissions/SubmissionsHandler.java b/src/main/java/org/patinanetwork/codebloom/common/submissions/SubmissionsHandler.java index 002cd8cc9..c0e2cab86 100644 --- a/src/main/java/org/patinanetwork/codebloom/common/submissions/SubmissionsHandler.java +++ b/src/main/java/org/patinanetwork/codebloom/common/submissions/SubmissionsHandler.java @@ -122,6 +122,12 @@ public ArrayList handleSubmissions( .questionLink("https://leetcode.com/problems/" + question.getTitleSlug()) .description(question.getQuestion()) .acceptanceRate(question.getAcceptanceRate()) + .topics(question.getTopics().stream() + .map(t -> QuestionTopic.builder() + .topicSlug(t.getSlug()) + .topic(LeetcodeTopicEnum.fromValue(t.getSlug())) + .build()) + .toList()) .build(); } diff --git a/src/test/java/org/patinanetwork/codebloom/common/submissions/SubmissionHandlerTest.java b/src/test/java/org/patinanetwork/codebloom/common/submissions/SubmissionHandlerTest.java index a50d171ac..b86206c65 100644 --- a/src/test/java/org/patinanetwork/codebloom/common/submissions/SubmissionHandlerTest.java +++ b/src/test/java/org/patinanetwork/codebloom/common/submissions/SubmissionHandlerTest.java @@ -13,8 +13,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.patinanetwork.codebloom.common.db.models.leaderboard.Leaderboard; -import org.patinanetwork.codebloom.common.db.models.question.QuestionDifficulty; -import org.patinanetwork.codebloom.common.db.models.question.bank.QuestionBank; import org.patinanetwork.codebloom.common.db.models.user.User; import org.patinanetwork.codebloom.common.db.models.user.UserWithScore; import org.patinanetwork.codebloom.common.db.repos.job.JobRepository; @@ -25,7 +23,9 @@ import org.patinanetwork.codebloom.common.db.repos.question.topic.QuestionTopicRepository; import org.patinanetwork.codebloom.common.db.repos.user.UserRepository; import org.patinanetwork.codebloom.common.db.repos.user.options.UserFilterOptions; +import org.patinanetwork.codebloom.common.leetcode.models.LeetcodeQuestion; import org.patinanetwork.codebloom.common.leetcode.models.LeetcodeSubmission; +import org.patinanetwork.codebloom.common.leetcode.models.LeetcodeTopicTag; import org.patinanetwork.codebloom.common.leetcode.throttled.ThrottledLeetcodeClient; import org.patinanetwork.codebloom.common.reporter.throttled.ThrottledReporter; import org.patinanetwork.codebloom.common.submissions.object.AcceptedSubmission; @@ -124,23 +124,26 @@ void testHandleSubmissionSuccess() { UserFilterOptions.DEFAULT)) .thenReturn(userWithScore); - QuestionBank bankQuestion = QuestionBank.builder() - .questionSlug("hello-world") - .questionDifficulty(QuestionDifficulty.Easy) + when(questionBankRepository.getQuestionBySlug("hello-world")).thenReturn(null); + LeetcodeTopicTag leetcodeTopicTag = + LeetcodeTopicTag.builder().name("String").slug("string").build(); + LeetcodeQuestion lcQuestion = LeetcodeQuestion.builder() + .questionId(1) + .titleSlug("hello-world") .questionTitle("Hello World") - .questionNumber(1) - .questionLink("https://leetcode.com/problems/hello-world") - .description("Print Hello World") + .difficulty("Easy") + .question("Print Hello World") .acceptanceRate(1.0f) - .topics(List.of()) + .topics(List.of(leetcodeTopicTag)) .build(); - when(questionBankRepository.getQuestionBySlug("hello-world")).thenReturn(bankQuestion); + when(leetcodeClient.findQuestionBySlugFast("hello-world")).thenReturn(lcQuestion); List result = submissionsHandler.handleSubmissions(List.of(submission), user, true); assertEquals(1, result.size()); + verify(leetcodeClient).findQuestionBySlugFast("hello-world"); verify(jobRepository).createJob(any()); verify(leaderboardRepository) .updateUserPointsFromLeaderboard(