feat: add worker operations and fix gitea actions
Some checks failed
docker-images / build-and-push (admin, admin, termi-astro-admin, admin/Dockerfile) (push) Successful in 29s
docker-images / build-and-push (backend, backend, termi-astro-backend, backend/Dockerfile) (push) Successful in 33m13s
docker-images / build-and-push (frontend, frontend, termi-astro-frontend, frontend/Dockerfile) (push) Successful in 58s
ui-regression / playwright-regression (push) Failing after 13m24s

This commit is contained in:
2026-04-02 03:43:37 +08:00
parent ee0bec4a78
commit a516be2e91
37 changed files with 3890 additions and 879 deletions

View File

@@ -1,6 +1,6 @@
import { expect, test, type Page } from '@playwright/test'
import { getDebugState, loginAdmin, resetMockState } from './helpers'
import { getDebugState, loginAdmin, MOCK_BASE_URL, resetMockState } from './helpers'
test.beforeEach(async ({ request }) => {
await resetMockState(request)
@@ -39,6 +39,7 @@ test('后台登录、导航与关键模块页面可加载', async ({ page }) =>
{ label: '评测', url: /\/reviews$/, text: '《漫长的季节》' },
{ label: '媒体库', url: /\/media$/, text: '漫长的季节封面' },
{ label: '订阅', url: /\/subscriptions$/, text: 'watcher@example.com' },
{ label: 'Workers', url: /\/workers$/, text: '异步 Worker 控制台' },
{ label: '审计', url: /\/audit$/, text: 'playwright-smoke' },
{ label: '设置', url: /\/settings$/, text: 'InitCool' },
]
@@ -50,6 +51,17 @@ test('后台登录、导航与关键模块页面可加载', async ({ page }) =>
}
})
test('后台 dashboard worker 健康卡片可跳转到带筛选的 workers', async ({ page }) => {
await loginAdmin(page)
await expect(page.locator('main')).toContainText('Worker 活动')
await page.getByTestId('dashboard-worker-card-failed').click()
await expect(page).toHaveURL(/\/workers\?status=failed$/)
await expect(page.locator('main')).toContainText('异步 Worker 控制台')
await expect(page.locator('main')).toContainText('failed')
})
test('后台可以审核评论和友链,并更新站点设置', async ({ page }) => {
await loginAdmin(page)
@@ -136,8 +148,21 @@ test('后台可完成订阅 CRUD、测试投递与 digest 入队', async ({ page
await expect(row).toContainText('Deep Regression Updated')
await row.getByTestId(/subscription-test-/).click()
await expect(page.getByTestId('subscriptions-last-job')).toBeVisible()
await page.getByTestId('subscriptions-last-job').click()
await expect(page).toHaveURL(/\/workers\?job=\d+$/)
await expect(page.locator('main')).toContainText('subscription.test')
await page.getByRole('link', { name: '订阅' }).click()
await page.getByTestId('subscriptions-send-weekly').click()
await expect(page.getByTestId('subscriptions-last-job')).toBeVisible()
await page.getByTestId('subscriptions-send-monthly').click()
await expect(page.getByTestId(/^subscription-delivery-job-/).first()).toBeVisible()
await page.getByTestId(/^subscription-delivery-job-/).first().click()
await expect(page).toHaveURL(/\/workers\?job=\d+$/)
await expect(page.locator('main')).toContainText('worker.notification_delivery')
await page.getByRole('link', { name: '订阅' }).click()
let state = await getDebugState(request)
expect(
@@ -146,6 +171,9 @@ test('后台可完成订阅 CRUD、测试投递与 digest 入队', async ({ page
).toBeTruthy()
expect(state.deliveries.some((item: { event_type: string }) => item.event_type === 'digest.weekly')).toBeTruthy()
expect(state.deliveries.some((item: { event_type: string }) => item.event_type === 'digest.monthly')).toBeTruthy()
expect(
state.worker_jobs.some((item: { worker_name: string }) => item.worker_name === 'worker.notification_delivery'),
).toBeTruthy()
await row.getByTestId(/subscription-delete-/).click()
await expect(row).toHaveCount(0)
@@ -156,6 +184,33 @@ test('后台可完成订阅 CRUD、测试投递与 digest 入队', async ({ page
).toBeFalsy()
})
test('后台可查看 worker 控制台并执行 digest / retry / job 重跑', async ({ page, request }) => {
await loginAdmin(page)
await page.getByRole('link', { name: 'Workers' }).click()
await page.getByTestId('workers-run-weekly').click()
await expect(page.locator('main')).toContainText('task.send_weekly_digest')
await page.getByTestId('workers-retry-job').click()
let state = await getDebugState(request)
expect(
state.worker_jobs.some((item: { worker_name: string }) => item.worker_name === 'task.send_weekly_digest'),
).toBeTruthy()
expect(
state.worker_jobs.some(
(item: { worker_name: string; parent_job_id: number | null }) =>
item.worker_name === 'task.send_weekly_digest' && item.parent_job_id !== null,
),
).toBeTruthy()
await page.getByTestId('workers-run-retry').click()
state = await getDebugState(request)
expect(
state.worker_jobs.some((item: { worker_name: string }) => item.worker_name === 'task.retry_deliveries'),
).toBeTruthy()
})
test('后台可完成文章创建、保存、版本恢复与删除', async ({ page, request }) => {
await loginAdmin(page)
await page.getByRole('link', { name: '文章' }).click()
@@ -209,6 +264,15 @@ test('后台可完成媒体库上传/元数据/替换/删除,并执行设置
await loginAdmin(page)
await page.getByRole('link', { name: '媒体库' }).click()
await page.getByTestId('media-remote-url').fill(`${MOCK_BASE_URL}/media-files/remote-playwright.svg`)
await page.getByTestId('media-remote-title').fill('Remote Playwright Cover')
await page.getByTestId('media-remote-download').click()
await expect(page.getByTestId('media-last-remote-job')).toBeVisible()
await page.getByTestId('media-last-remote-job').click()
await expect(page).toHaveURL(/\/workers\?job=\d+$/)
await expect(page.locator('main')).toContainText('worker.download_media')
await page.getByRole('link', { name: '媒体库' }).click()
await page.getByTestId('media-upload-input').setInputFiles([
buildSvgPayload('deep-regression-cover.svg', 'deep-upload'),
])
@@ -222,6 +286,13 @@ test('后台可完成媒体库上传/元数据/替换/删除,并执行设置
await page.getByTestId('media-save-metadata').click()
let state = await getDebugState(request)
expect(
state.media.some(
(item: { title: string; key: string }) =>
item.title === 'Remote Playwright Cover' &&
String(item.key || '').includes('post-covers/'),
),
).toBeTruthy()
expect(
state.media.some(
(item: { title: string; alt_text: string; tags: string[] }) =>