Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
WITH target_users AS (
SELECT u.id AS user_id
FROM users u
WHERE NOT EXISTS (
SELECT 1
FROM ask_chat_wallet_logs l
WHERE l.user_id = u.id
AND l.reason = 'INITIAL_FREE_GRANT'
)
),
granted_wallets AS (
INSERT INTO ask_chat_wallets (
user_id,
free_turn_balance,
paid_turn_balance,
version,
created_at,
updated_at
)
SELECT
user_id,
3,
0,
0,
NOW(),
NOW()
FROM target_users
ON CONFLICT (user_id) DO UPDATE
SET free_turn_balance = ask_chat_wallets.free_turn_balance + EXCLUDED.free_turn_balance,
updated_at = NOW(),
version = ask_chat_wallets.version + 1
RETURNING
user_id,
free_turn_balance,
paid_turn_balance
)
INSERT INTO ask_chat_wallet_logs (
user_id,
session_id,
message_id,
free_turn_delta,
paid_turn_delta,
free_turn_balance_after,
paid_turn_balance_after,
reason,
status,
ref_type,
ref_id,
idempotency_key,
created_at
)
SELECT
user_id,
NULL,
NULL,
3,
0,
free_turn_balance,
paid_turn_balance,
'INITIAL_FREE_GRANT',
'CONFIRMED',
'SIGNUP',
user_id,
'ask-chat-initial-free-' || user_id,
NOW()
FROM granted_wallets;
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.devkor.ifive.nadab.domain.askchat.core.repository;

import com.devkor.ifive.nadab.domain.askchat.core.entity.AskChatWallet;
import com.devkor.ifive.nadab.domain.askchat.core.entity.AskChatWalletLog;
import com.devkor.ifive.nadab.domain.askchat.core.entity.AskChatWalletLogReason;
import com.devkor.ifive.nadab.domain.user.core.entity.User;
import com.devkor.ifive.nadab.infra.builder.UserBuilder;
import com.devkor.ifive.nadab.infra.db.PostgresIntegrationTestSupport;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ActiveProfiles;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

@DataJpaTest
@ActiveProfiles("test")
class AskChatInitialFreeTurnBackfillMigrationTest extends PostgresIntegrationTestSupport {

@Autowired
AskChatWalletRepository askChatWalletRepository;

@Autowired
AskChatWalletLogRepository askChatWalletLogRepository;

@Autowired
TestEntityManager em;

@Autowired
JdbcTemplate jdbcTemplate;

@Value("classpath:db/migration/V20260831_1200__IS_repair_ask_chat_initial_free_turn_backfill.sql")
Resource migration;

@Test
void backfill_grants_missing_initial_turns_once_and_preserves_existing_balances() throws IOException {
User userWithoutWallet = new UserBuilder(em).build();

User userWithWallet = new UserBuilder(em).build();
askChatWalletRepository.save(AskChatWallet.create(userWithWallet, 0, 7));

User alreadyGrantedUser = new UserBuilder(em).build();
askChatWalletRepository.save(AskChatWallet.create(alreadyGrantedUser, 1, 5));
askChatWalletLogRepository.save(AskChatWalletLog.createConfirmed(
alreadyGrantedUser,
null,
null,
3,
0,
3,
0,
AskChatWalletLogReason.INITIAL_FREE_GRANT,
"SIGNUP",
alreadyGrantedUser.getId(),
"ask-chat-initial-free-" + alreadyGrantedUser.getId()
));
em.flush();

String migrationSql = migration.getContentAsString(StandardCharsets.UTF_8);
jdbcTemplate.execute(migrationSql);
jdbcTemplate.execute(migrationSql);
em.clear();

assertWalletBalances(userWithoutWallet.getId(), 3, 0);
assertWalletBalances(userWithWallet.getId(), 3, 7);
assertWalletBalances(alreadyGrantedUser.getId(), 1, 5);
assertInitialGrantLog(userWithoutWallet.getId(), 3, 0);
assertInitialGrantLog(userWithWallet.getId(), 3, 7);
assertInitialGrantLog(alreadyGrantedUser.getId(), 3, 0);
}

private void assertWalletBalances(Long userId, int expectedFreeTurns, int expectedPaidTurns) {
AskChatWallet wallet = askChatWalletRepository.findByUserId(userId).orElseThrow();
assertThat(wallet.getFreeTurnBalance()).isEqualTo(expectedFreeTurns);
assertThat(wallet.getPaidTurnBalance()).isEqualTo(expectedPaidTurns);
}

private void assertInitialGrantLog(Long userId, int expectedFreeBalance, int expectedPaidBalance) {
List<AskChatWalletLog> initialGrantLogs = askChatWalletLogRepository
.findAllByUserIdOrderByCreatedAtDesc(userId).stream()
.filter(log -> log.getReason() == AskChatWalletLogReason.INITIAL_FREE_GRANT)
.toList();

assertThat(initialGrantLogs).hasSize(1);
AskChatWalletLog initialGrantLog = initialGrantLogs.getFirst();
assertThat(initialGrantLog.getFreeTurnDelta()).isEqualTo(3);
assertThat(initialGrantLog.getPaidTurnDelta()).isZero();
assertThat(initialGrantLog.getFreeTurnBalanceAfter()).isEqualTo(expectedFreeBalance);
assertThat(initialGrantLog.getPaidTurnBalanceAfter()).isEqualTo(expectedPaidBalance);
}
}
Loading