Skip to content
1 change: 1 addition & 0 deletions slackjudge/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ out/
### VS Code ###
.vscode/
.env
/logs/*
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class ProblemJdbcRepository {
* @date 25. 12. 8.
*
==========================**/
public void updateProblemSolved(LocalDateTime batchTime, Long userId, Integer problemNumber){
public void updateProblemSolved(LocalDateTime batchTime, Long userId, Integer problemNumber) {
String sql = """
INSERT INTO users_problem (user_id, problem_id, is_solved, solved_time)
VALUES (?, ?, true, ?)
Expand All @@ -41,16 +41,16 @@ ON CONFLICT (user_id, problem_id)

public void batchInsertProblems(LocalDateTime snapshotAt, Long userId, List<Integer> problemIds) {
String sql = """
INSERT INTO users_problem (user_id, problem_id, is_solved, solved_time)
VALUES (?, ?, true, ?)
ON CONFLICT (user_id, problem_id) DO NOTHING
""";
INSERT INTO users_problem (user_id, problem_id, is_solved, solved_time)
VALUES (?, ?, true, ?)
ON CONFLICT (user_id, problem_id) DO NOTHING
""";

jdbcTemplate.batchUpdate(sql, problemIds, problemIds.size(),
(ps, problemId) -> {
ps.setLong(1, userId);
ps.setInt(2, problemId);
ps.setTimestamp(3, java.sql.Timestamp.valueOf(snapshotAt));
});
jdbcTemplate.batchUpdate(sql, problemIds, problemIds.size(),
(ps, problemId) -> {
ps.setLong(1, userId);
ps.setInt(2, problemId);
ps.setTimestamp(3, java.sql.Timestamp.valueOf(snapshotAt));
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package store.slackjudge.batch.infra.mongo.document;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

사용되지 않는 Assertions import 문입니다. 코드를 깔끔하게 유지하기 위해 제거하는 것이 좋습니다.


class SnapShotIdTest {

@Test
@DisplayName("of()는 동일한 값(bojId, snapShotAt)을 가진 인스턴스를 생성")
void of_createsInstance() {
LocalDateTime t = LocalDateTime.of(2025, 12, 16, 10, 0);
SnapShotId id = SnapShotId.of("test", t);

assertThat(id.getBojId()).isEqualTo("test");
assertThat(id.getSnapShotAt()).isEqualTo(t);
}

@Test
@DisplayName("같은 필드 값을 가지면 equals는 true이고 hashCode도 같다")
void equalsAndHashCode_sameValues() {
LocalDateTime t = LocalDateTime.of(2025, 12, 16, 10, 0);

SnapShotId a = SnapShotId.of("boj", t);
SnapShotId b = SnapShotId.of("boj", t);

assertThat(a).isEqualTo(b);
assertThat(a.hashCode()).isEqualTo(b.hashCode());
}

@Test
@DisplayName("bojId가 다르면 equals는 false")
void equals_false_whenDifferentBojId() {
LocalDateTime t = LocalDateTime.of(2025, 12, 16, 10, 0);

SnapShotId a = SnapShotId.of("bojA", t);
SnapShotId b = SnapShotId.of("bojB", t);

assertThat(a).isNotEqualTo(b);
}

@Test
@DisplayName("snapShotAt이 다르면 equals는 false")
void equals_false_whenDifferentSnapShotAt() {
LocalDateTime t1 = LocalDateTime.of(2025, 12, 16, 10, 0);
LocalDateTime t2 = LocalDateTime.of(2025, 12, 16, 11, 0);

SnapShotId a = SnapShotId.of("boj", t1);
SnapShotId b = SnapShotId.of("boj", t2);

assertThat(a).isNotEqualTo(b);
}

@Test
@DisplayName("null 또는 다른 타입과는 equals가 false")
void equals_false_whenNullOrDifferentType() {
SnapShotId a = SnapShotId.of("boj", LocalDateTime.of(2025, 12, 16, 10, 0));

assertThat(a.equals(null)).isFalse();
assertThat(a.equals("not-a-snapshot-id")).isFalse();
}

@Test
@DisplayName("HashSet에서 동일 키는 중복 저장되지 않는다")
void hashSet_deduplicatesByEqualsAndHashCode() {
LocalDateTime t = LocalDateTime.of(2025, 12, 16, 10, 0);

SnapShotId a = SnapShotId.of("boj", t);
SnapShotId b = SnapShotId.of("boj", t);

Set<SnapShotId> set = new HashSet<>();
set.add(a);
set.add(b);

assertThat(set).hasSize(1);
assertThat(set).contains(a);
}

@Test
@DisplayName("HashMap 키로 사용 시 동일 키로 조회가 가능")
void hashMap_keyLookupWorks() {
LocalDateTime t = LocalDateTime.of(2025, 12, 16, 10, 0);

SnapShotId key1 = SnapShotId.of("boj", t);
SnapShotId key2 = SnapShotId.of("boj", t); // equals true

Map<SnapShotId, String> map = new HashMap<>();
map.put(key1, "value");

assertThat(map.get(key2)).isEqualTo("value");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
import store.slackjudge.batch.PostgresTestContainer;

import java.time.LocalDateTime;
import java.util.List;

import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;

@ActiveProfiles("test")
@EnablePostgresTest
@DataJpaTest
Expand Down Expand Up @@ -155,4 +157,56 @@ INSERT INTO users_problem (user_id, problem_id, is_solved, solved_time)
assertThat(usersProblem.userId).isEqualTo(userId);
}

@DisplayName("여러 문제를 batch로 INSERT(중복은 무시)")
@Test
void batchInsertProblems() {
// given
Long userId = 12L;
LocalDateTime snapshotAt = LocalDateTime.of(2025, 1, 1, 10, 0);
List<Integer> problemIds = List.of(1000, 1001);

// 기존 데이터 하나 삽입(중복 검증용)
jdbcTemplate.update("""
INSERT INTO users_problem (user_id, problem_id, is_solved, solved_time)
VALUES (?, ?, true, ?)
""", userId, 1000, LocalDateTime.of(2000, 1, 1, 0, 0));

// when
repository.batchInsertProblems(snapshotAt, userId, problemIds);

// then
String sql = """
SELECT user_id, problem_id, is_solved, solved_time
FROM users_problem
WHERE user_id = ?
ORDER BY problem_id
""";

List<UsersProblem> results = jdbcTemplate.query(
sql,
(rs, rowNum) -> new UsersProblem(
rs.getLong("user_id"),
rs.getInt("problem_id"),
rs.getBoolean("is_solved"),
rs.getTimestamp("solved_time").toLocalDateTime()
),
userId
);

// problem_id = 1000,1001 총 2개
assertThat(results).hasSize(2);

// 1000번 문제는 기존 데이터 유지 (ON CONFLICT DO NOTHING)
UsersProblem first = results.get(0);
assertThat(first.problemId()).isEqualTo(1000);
assertThat(first.solvedTime())
.isEqualTo(LocalDateTime.of(2000, 1, 1, 0, 0));

// 1001번 문제는 새로 INSERT
UsersProblem second = results.get(1);
assertThat(second.problemId()).isEqualTo(1001);
assertThat(second.solvedTime()).isEqualTo(snapshotAt);
assertThat(second.isSolved()).isTrue();
}

}
Loading