v1.12.30 긴 접속 시간 표시와 중단 후 새 방문 처리 개선

This commit is contained in:
2026-10-21 18:45:44 +09:00
parent 51d5de148f
commit 7b6fc45bf3
19 changed files with 310 additions and 68 deletions

View File

@@ -428,6 +428,42 @@ test('실시간 접속 요약은 표시 제한 밖의 활성 탭까지 집계하
}
})
test('긴 접속 시간은 실시간에 표시하고 평균 체류 집계 상한을 유지한다', async ({ request: anonymousApi }) => {
const slug = 'realtime-duration-api-test'
const [post] = await database`
INSERT INTO posts (title, slug, content, status, author_id, published_at)
VALUES ('긴 접속 시간 검증', ${slug}, '시간 검증', 'published', ${adminUser.userId}, now())
RETURNING id
`
const heartbeat = {
path: `/post/${slug}`, postSlug: slug, clientSessionId: 'duration-api-client-session',
contentSessionId: 'duration-api-content-session', durationSeconds: 1810, maxScrollRatio: 0
}
try {
expect((await anonymousApi.post('/api/analytics/heartbeat', { data: heartbeat })).status()).toBe(200)
heartbeat.durationSeconds = 7200
expect((await anonymousApi.post('/api/analytics/heartbeat', { data: heartbeat })).status()).toBe(200)
const realtime = await adminApi.get('/admin/api/analytics/realtime')
expect((await realtime.json()).sessions.find((session) => session.postSlug === slug)?.durationSeconds).toBe(7200)
const sessions = await database`SELECT duration_seconds FROM analytics_content_sessions WHERE post_id = ${post.id}`
expect(sessions).toHaveLength(1)
expect(sessions[0].duration_seconds).toBe(1800)
const daily = await database`SELECT total_engaged_seconds FROM post_analytics_daily WHERE post_id = ${post.id}`
expect(daily[0].total_engaged_seconds).toBe(1800)
heartbeat.durationSeconds = 2147483648
expect((await anonymousApi.post('/api/analytics/heartbeat', { data: heartbeat })).status()).toBe(400)
heartbeat.contentSessionId = 'duration-api-return-visit'
heartbeat.durationSeconds = 0
expect((await anonymousApi.post('/api/analytics/heartbeat', { data: heartbeat })).status()).toBe(200)
const restarted = await adminApi.get('/admin/api/analytics/realtime')
expect((await restarted.json()).sessions.find((session) => session.postSlug === slug)?.durationSeconds).toBe(0)
} finally {
await database`DELETE FROM analytics_active_sessions WHERE post_slug = ${slug}`
await database`DELETE FROM analytics_content_sessions WHERE post_id = ${post.id}`
await database`DELETE FROM posts WHERE id = ${post.id}`
}
})
test('미디어 카탈로그 reconcile과 DB 목록 계약 및 fallback을 유지한다', async () => {
const uploadRoot = process.env.SORI_TEST_UPLOAD_ROOT
const mediaDirectory = join(uploadRoot, 'posts', 'catalog-api-test')