fix: repair existing test failures

This commit is contained in:
2026-08-13 00:52:22 +00:00
parent 6da7d2f899
commit d133db32eb
24 changed files with 386 additions and 116 deletions
+6 -5
View File
@@ -27,6 +27,7 @@ interface AuctionRow {
detail: unknown;
status: AuctionStatus;
closeAt: Date;
latestEventId: string;
}
interface AuctionBidRow {
@@ -101,7 +102,8 @@ const loadAuction = async (prisma: QueryClient, auctionId: number): Promise<Auct
host_general_id as "hostGeneralId",
detail,
status,
close_at as "closeAt"
close_at as "closeAt",
latest_event_id as "latestEventId"
FROM auction
WHERE id = ${auctionId}
FOR UPDATE
@@ -375,6 +377,8 @@ export const createAuctionBidder = async (options: {
`
);
// 같은 논리 tick의 연속 입찰은 시각이 같으므로, UUID 정렬이 아니라
// 읽어 둔 이벤트 ID를 버전 토큰으로 사용해 경합만 거절한다.
const updated = await tx.$executeRaw(
GamePrisma.sql`
UPDATE auction
@@ -385,10 +389,7 @@ export const createAuctionBidder = async (options: {
updated_at = ${eventAt}
WHERE id = ${command.auctionId}
AND status = 'OPEN'
AND (
latest_event_at < ${eventAt}
OR (latest_event_at = ${eventAt} AND latest_event_id < ${eventId})
)
AND latest_event_id = ${auction.latestEventId}
`
);
@@ -301,6 +301,7 @@ const zNpcPossessGeneral = z
ownerLegacyPenalty: zRecord.optional(),
generalId: z.number().int().positive(),
tokenNonce: z.number().int().nonnegative(),
acceptedGameAt: z.string().refine(isCanonicalIsoTimestamp).optional(),
})
.strict();
@@ -460,6 +460,7 @@ export const possessNpcGeneral = async (options: {
acceptedAt: Date;
}): Promise<{ ok: true; generalId: number }> => {
const { db, world, worldState, userId, generalId, acceptedAt } = options;
// queue 대기 중 만료된 token도 enqueue 시점에는 유효했으므로 저장된 논리 수락 시각으로 다시 검증한다.
const tokenAcceptedAt = truncateToSeconds(acceptedAt);
requireNpcPossessionWorld(worldState);
await lockNpcPossession(db, userId);
@@ -294,7 +294,9 @@ async function handleNpcPossessGeneral(
throw new Error('NPC possession world state is missing.');
}
const operationalAcceptedAt = await resolveCommandAcceptedAt(db, command);
const acceptedAt = ctx.world.getGameNow(operationalAcceptedAt);
const acceptedAt = command.acceptedGameAt
? new Date(command.acceptedGameAt)
: ctx.world.getGameNow(operationalAcceptedAt);
try {
return {
type: 'npcPossessGeneral',