feat: complete release preparation, 10+ ad mediation, CI/CD, and docker deployment
Some checks failed
CI Pipeline / Code Quality & Typecheck (push) Waiting to run
CI Pipeline / Test Suite (macos-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (ubuntu-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (windows-latest) (push) Blocked by required conditions
CI Pipeline / Build Validation (admin) (push) Blocked by required conditions
CI Pipeline / Build Validation (desktop) (push) Blocked by required conditions
Deploy Landing Page / deploy (push) Blocked by required conditions
Deploy Landing Page / build (push) Waiting to run
Release & Packaging Pipeline / Build & Publish Admin Docker Image (push) Failing after 8s
Release & Code Signing CA Pipeline / build-and-sign-windows (push) Failing after 1m51s
Build macOS / Build & Package (macOS) (push) Failing after 4s
Build macOS / Build & Package (macOS)-1 (push) Failing after 5s
Release & Code Signing CA Pipeline / build-and-sign-macos (push) Failing after 3s
Release & Packaging Pipeline / Package macOS Desktop App (push) Failing after 4s
Release & Packaging Pipeline / Package Windows Desktop App (push) Failing after 2m28s
Release & Packaging Pipeline / Publish Official GitHub Release (push) Has been skipped
Some checks failed
CI Pipeline / Code Quality & Typecheck (push) Waiting to run
CI Pipeline / Test Suite (macos-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (ubuntu-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (windows-latest) (push) Blocked by required conditions
CI Pipeline / Build Validation (admin) (push) Blocked by required conditions
CI Pipeline / Build Validation (desktop) (push) Blocked by required conditions
Deploy Landing Page / deploy (push) Blocked by required conditions
Deploy Landing Page / build (push) Waiting to run
Release & Packaging Pipeline / Build & Publish Admin Docker Image (push) Failing after 8s
Release & Code Signing CA Pipeline / build-and-sign-windows (push) Failing after 1m51s
Build macOS / Build & Package (macOS) (push) Failing after 4s
Build macOS / Build & Package (macOS)-1 (push) Failing after 5s
Release & Code Signing CA Pipeline / build-and-sign-macos (push) Failing after 3s
Release & Packaging Pipeline / Package macOS Desktop App (push) Failing after 4s
Release & Packaging Pipeline / Package Windows Desktop App (push) Failing after 2m28s
Release & Packaging Pipeline / Publish Official GitHub Release (push) Has been skipped
This commit is contained in:
parent
5cd1de6859
commit
708e20f747
406 changed files with 42464 additions and 6199 deletions
132
apps/desktop/tests/red/silent-errors.usecase.test.ts
Normal file
132
apps/desktop/tests/red/silent-errors.usecase.test.ts
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { ErrorCode } from '@d3ro/core/errors'
|
||||
import { Feature } from '@d3ro/core/types'
|
||||
import { IPC_CHANNELS } from '@d3ro/core/ipc-channels'
|
||||
import { getHistoryService } from '../../src/main/services/HistoryService'
|
||||
import { getLicenseService } from '../../src/main/services/LicenseService'
|
||||
import { getAutoLaunchService } from '../../src/main/services/AutoLaunchService'
|
||||
import { getCaptionService } from '../../src/main/services/CaptionService'
|
||||
import { configGet, configSet } from '../../src/main/services/ConfigService'
|
||||
import { registerLLMHandlers } from '../../src/main/ipc/llm-handlers'
|
||||
import { registerHistoryHandlers } from '../../src/main/ipc/history-handlers'
|
||||
import { registerLicenseHandlers } from '../../src/main/ipc/license-handlers'
|
||||
import { historyInput, invokeIpc, useRedHarness } from './harness'
|
||||
import { FX, USER_TEXT } from './fixtures'
|
||||
|
||||
useRedHarness()
|
||||
|
||||
describe('유스케이스: 사일런트 에러 / 폴백 성공 위장 탐지', () => {
|
||||
it('자동 제목 LLM 실패 후 히스토리는 completed 로 남되 title 공백을 성공 제목으로 쓰지 않는다', async () => {
|
||||
const entry = getHistoryService().create(historyInput({ originalText: USER_TEXT.KO }))
|
||||
const title = await getHistoryService().generateTitle(entry.id)
|
||||
expect(title === null || title.length > 0).toBe(true)
|
||||
if (title === null) {
|
||||
expect(getHistoryService().getById(entry.id)?.title ?? null).toBeNull()
|
||||
}
|
||||
})
|
||||
|
||||
it('라이선스 사용량 DB 가 열려 있을 때 getUsage 가 실패를 0으로 숨기지 않는다', () => {
|
||||
const svc = getLicenseService()
|
||||
svc.initialize()
|
||||
const usage = svc.getUsage(Feature.DICTATION)
|
||||
expect(usage.used).toBe(0)
|
||||
expect(usage.limit).toBe(-1)
|
||||
})
|
||||
|
||||
it('자동 시작 OS API 실패 시 config 와 OS 가 어긋난 채 성공으로 끝나지 않는다', () => {
|
||||
const errors: string[] = []
|
||||
const orig = console.error
|
||||
try {
|
||||
getAutoLaunchService().setEnabled(true)
|
||||
expect(configGet('autoLaunch')).toBe(true)
|
||||
} catch (err) {
|
||||
errors.push(String(err))
|
||||
expect(configGet('autoLaunch')).not.toBe(true)
|
||||
}
|
||||
void orig
|
||||
})
|
||||
|
||||
it('캡션 초기 상태는 inactive 이며 start 실패가 active+빈세그먼트로 위장되지 않는다', async () => {
|
||||
expect(getCaptionService().getState()).toBe('inactive')
|
||||
try {
|
||||
await getCaptionService().start()
|
||||
expect(['starting', 'active', 'inactive']).toContain(getCaptionService().getState())
|
||||
} catch (err) {
|
||||
expect(getCaptionService().getState()).toBe('inactive')
|
||||
expect(err).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
it('IPC llm:process 가 원문을 processedText 로 그대로 돌려주며 success 하지 않는다 (미인증)', async () => {
|
||||
configSet('llmBackend', 'online')
|
||||
configSet('authToken', null)
|
||||
registerLLMHandlers()
|
||||
const res = await invokeIpc(IPC_CHANNELS.LLM.PROCESS, {
|
||||
text: USER_TEXT.KO,
|
||||
action: 'refine',
|
||||
})
|
||||
expect(res.success).toBe(false)
|
||||
if (res.success) {
|
||||
expect(res.data.processedText).not.toBe(USER_TEXT.KO)
|
||||
}
|
||||
})
|
||||
|
||||
it('IPC 히스토리 삭제가 없는 id 를 success 로 위장하지 않는다', async () => {
|
||||
registerHistoryHandlers()
|
||||
const res = await invokeIpc(IPC_CHANNELS.HISTORY.DELETE, { id: 'ghost' })
|
||||
expect(res.success).toBe(false)
|
||||
})
|
||||
|
||||
it('프리미엄 상태 IPC 가 로그인 없이 isAvailable:true 로 위장하지 않는다', async () => {
|
||||
registerLLMHandlers()
|
||||
const res = await invokeIpc(IPC_CHANNELS.LLM.PREMIUM_GET_STATUS)
|
||||
expect(res.success).toBe(true)
|
||||
if (res.success) expect(res.data.isAvailable).toBe(false)
|
||||
})
|
||||
|
||||
it('라이선스 빈 키 IPC 활성화가 tier=pro 로 위장하지 않는다', async () => {
|
||||
getLicenseService().initialize()
|
||||
registerLicenseHandlers()
|
||||
const res = await invokeIpc(IPC_CHANNELS.LICENSE.ACTIVATE, { licenseKey: '' })
|
||||
expect(res.success).toBe(true)
|
||||
if (res.success) {
|
||||
expect(res.data.success).toBe(false)
|
||||
expect(res.data.tier).toBe('free')
|
||||
}
|
||||
expect(getLicenseService().tier).toBe('free')
|
||||
})
|
||||
|
||||
it('잘못된 키 활성화 메시지가 비어 있지 않다', async () => {
|
||||
const result = await getLicenseService().activate(FX.LICENSE_BAD)
|
||||
expect(result.success).toBe(false)
|
||||
expect(result.message.length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
it('configSet 후 같은 키 get 이 이전 기본값을 반환하지 않는다', () => {
|
||||
configSet('soundEnabled', false)
|
||||
expect(configGet('soundEnabled')).toBe(false)
|
||||
})
|
||||
|
||||
it('authToken 만료 클리어 후 Online 경로가 캐시된 성공을 내지 않는다', async () => {
|
||||
configSet('authToken', FX.AUTH_TOKEN)
|
||||
configSet('llmBackend', 'online')
|
||||
registerLLMHandlers()
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi.fn(async () => ({ ok: false, status: 401, json: async () => ({}) })),
|
||||
)
|
||||
const res = await invokeIpc(IPC_CHANNELS.LLM.PROCESS, { text: 'x', action: 'refine' })
|
||||
expect(res.success).toBe(false)
|
||||
expect(configGet('authToken')).toBeNull()
|
||||
})
|
||||
|
||||
it('캡션 setConfig 후 getConfig 가 반영된다', () => {
|
||||
getCaptionService().setConfig({ fontSize: 22 })
|
||||
expect(getCaptionService().getConfig().fontSize).toBe(22)
|
||||
})
|
||||
|
||||
it('캡션 stop 은 inactive 에서 던져도 상태가 깨지지 않는다', async () => {
|
||||
await getCaptionService().stop()
|
||||
expect(getCaptionService().getState()).toBe('inactive')
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue