This commit is contained in:
2026-10-19 00:57:56 +07:00
parent 5635b8b5b1
commit 7fa0004ab9
7 changed files with 20 additions and 15 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ test('журнал сохраняет только разрешённые пол
})
log.record('request-error', { path: '/api/games/7?token=SECRET', reason: 'ENOTFOUND' })
log.record('request-error', { path: '/api/games/{id}', reason: 'SECRET' })
log.record('request-error', { path: '/api/scenarios/{id}', reason: 'AUTH', token: 'SECRET' })
log.record('request-error', { path: '/api/scenarios/{id}/full', reason: 'AUTH', token: 'SECRET' })
const content = await readFile(join(dir, 'diagnostic.jsonl'), 'utf8')
assert.equal(content.includes('SECRET'), false)
const lines = content
+1 -1
View File
@@ -28,7 +28,7 @@ const fields: Partial<Record<Event, Record<string, readonly string[]>>> = {
],
},
'request-error': {
path: ['/api/games/{id}', '/api/games/{id}/screen-plan', '/api/games/{id}/music', '/api/scenarios/{id}'],
path: ['/api/games/{id}', '/api/games/{id}/screen-plan', '/api/games/{id}/music', '/api/scenarios/{id}/full'],
reason: [...NETWORK_ERROR_CODES, 'AUTH', 'HTTP', 'INVALID_JSON', 'SESSION_CHANGED'],
},
'media-error': { channel: ['main', 'music', 'scene'] },
+1 -1
View File
@@ -163,7 +163,7 @@ async function fixture(t: TestContext, secure = true) {
return json({}, 401)
if (model.refreshExpired) return json({}, 401)
if (path === '/api/users/me') return json({ user: { roles: model.roles } })
if (path === '/api/scenarios/23') return model.scenarioError ? json({}, model.scenarioError) : json({ scenario: { ID: 23, image: 'https://media.test/cover.png', story: { background: 'https://media.test/background.png', introduction: { audio: model.introURL }, questions: [{ answer: 'PRIVATE-ANSWER' }] } } })
if (path === '/api/scenarios/23/full') return model.scenarioError ? json({}, model.scenarioError) : json({ scenario: { ID: 23, image: 'https://media.test/cover.png', story: { background: 'https://media.test/background.png', introduction: { audio: model.introURL }, questions: [{ answer: 'PRIVATE-ANSWER' }] } } })
if (path === '/api/games')
return json({
games: [
+1 -1
View File
@@ -136,7 +136,7 @@ export function requestErrorContext(
if (contextualErrors.has(result)) return result
const names: Record<string, string> = {
GetGame: 'Описание игры',
GetScenario: 'Сценарий игры',
GetFullScenario: 'Сценарий игры',
GetGameMusic: 'Музыка игры',
GetScreenPlan: 'План экранов',
SaveScreenPlan: 'Сохранение плана',
+8 -6
View File
@@ -195,11 +195,13 @@ test('полный сценарий добавляет фон и введени
const pkg = await prepareScreenPackage({ server, gameId: 7, token: 'SECRET', onIncomplete: () => { incomplete = true } }, async (input) => {
const url = String(input)
calls.push(url)
return Response.json(url.endsWith('/scenarios/23') ? full : url.endsWith('/music') ? music : url.endsWith('/screen-plan') ? {} : game)
if (url.endsWith('/scenarios/23')) return Response.json({ scenario: { ID: 23, image: 'cover.png' } })
return Response.json(url.endsWith('/scenarios/23/full') ? full : url.endsWith('/music') ? music : url.endsWith('/screen-plan') ? {} : game)
})
assert.equal(pkg.assets.length, 16)
assert.ok(pkg.media.background && pkg.media.intro)
assert.ok(calls.includes(`${server}/api/scenarios/23`))
assert.ok(calls.includes(`${server}/api/scenarios/23/full`))
assert.ok(!calls.includes(`${server}/api/scenarios/23`))
assert.equal(incomplete, false)
assert.equal(JSON.stringify(pkg).includes('PRIVATE-ANSWER'), false)
assert.deepEqual(parseScreenPackage(pkg), pkg)
@@ -212,17 +214,17 @@ test('недоступный или неполный сценарий преду
const failures: unknown[] = []
const pkg = await prepareScreenPackage({ server, gameId: 7, token: 'SECRET', onIncomplete: () => { incomplete = true }, onRequestFailure: (failure) => failures.push(failure) }, async (input) => {
const url = String(input)
return url.endsWith('/scenarios/23') ? Response.json({ scenario: { ID: 23 } }, { status }) : Response.json(url.endsWith('/games/7') ? game : {})
return url.endsWith('/scenarios/23/full') ? Response.json({ scenario: { ID: 23 } }, { status }) : Response.json(url.endsWith('/games/7') ? game : {})
})
assert.equal(incomplete, true)
assert.equal(pkg.assets.length, 1)
if (status !== 200) assert.deepEqual(failures, [{ path: '/api/scenarios/{id}', reason: status === 403 ? 'AUTH' : 'HTTP' }])
if (status !== 200) assert.deepEqual(failures, [{ path: '/api/scenarios/{id}/full', reason: status === 403 ? 'AUTH' : 'HTTP' }])
}
await assert.rejects(prepareScreenPackage({ server, gameId: 7, token: 'SECRET' }, async (input) =>
Response.json(String(input).endsWith('/scenarios/23') ? { scenario: { ID: 24 } } : String(input).endsWith('/games/7') ? game : {})),
Response.json(String(input).endsWith('/scenarios/23/full') ? { scenario: { ID: 24 } } : String(input).endsWith('/games/7') ? game : {})),
/Сценарий игры.*GET \/api\/scenarios\/23.*другой сценарий/)
await assert.rejects(prepareScreenPackage({ server, gameId: 7, token: 'SECRET' }, async (input) =>
Response.json(String(input).endsWith('/scenarios/23') ? null : String(input).endsWith('/games/7') ? game : {})),
Response.json(String(input).endsWith('/scenarios/23/full') ? null : String(input).endsWith('/games/7') ? game : {})),
/Сценарий игры.*scenarioResponse/)
})
+4 -4
View File
@@ -329,7 +329,7 @@ export async function prepareScreenPackage(
: meta.method === 'GetScreenPlan'
? '/screen-plan'
: ''
const safePath = meta.method === 'GetScenario' ? '/api/scenarios/{id}' : `/api/games/{id}${suffix}`
const safePath = meta.method === 'GetFullScenario' ? '/api/scenarios/{id}/full' : `/api/games/{id}${suffix}`
let response: Response
try {
response = await fetchAPI(`${server}/${requestData.path}`, {
@@ -397,7 +397,7 @@ export async function prepareScreenPackage(
let response
let received = false
try {
response = await client.GetScenario({ ID: scenarioId })
response = await client.GetFullScenario({ ID: scenarioId })
received = true
} catch (error) {
if (signal.aborted || error instanceof SessionChangedError || (error instanceof ApiError && error.status === 401)) throw error
@@ -413,7 +413,7 @@ export async function prepareScreenPackage(
gameData.scenario = { ...scenario, ...full, story: full.story }
if (full.story == null) request.onIncomplete?.()
} catch (error) {
throw requestErrorContext(error, 'GetScenario', 'GET', `/api/scenarios/${scenarioId}`)
throw requestErrorContext(error, 'GetFullScenario', 'GET', `/api/scenarios/${scenarioId}/full`)
}
}
} else if (scenario.story == null) request.onIncomplete?.()
@@ -422,7 +422,7 @@ export async function prepareScreenPackage(
} catch (error) {
const field = error instanceof ResponseFieldError ? error.field : 'game'
if (fullScenarioId !== null && field.startsWith('game.scenario.'))
throw requestErrorContext(error, 'GetScenario', 'GET', `/api/scenarios/${fullScenarioId}`)
throw requestErrorContext(error, 'GetFullScenario', 'GET', `/api/scenarios/${fullScenarioId}/full`)
const state = field.startsWith('state.')
const suffix = field.startsWith('plan')
? '/screen-plan'
+4 -1
View File
@@ -24,7 +24,8 @@ module.exports = async ({ evaluate, poll }) => {
if (request.url === '/api/games/7/music') return send({ gamePlaylistId: 1, playlists: [{ ID: 1, name: 'Музыка', tracks:
Array.from({ length: 13 }, (_, index) => ({ ID: index + 1, name: `Трек ${index}`, filename: `track-${index}.wav` })),
}] })
if (request.url === '/api/scenarios/23') return send({ scenario: { ID: 23, image: 'cover.png', story: { background: 'background.png', introduction: { audio: 'intro.wav' } } } }, scenarioError ? 403 : 200)
if (request.url === '/api/scenarios/23') return send({ scenario: { ID: 23, image: 'cover.png' } })
if (request.url === '/api/scenarios/23/full') return send({ scenario: { ID: 23, image: 'cover.png', story: { background: 'background.png', introduction: { audio: 'intro.wav' } } } }, scenarioError ? 403 : 200)
if (request.url.startsWith('/api/files/')) {
response.writeHead(200, { 'Content-Type': request.url.endsWith('.png') ? 'image/png' : 'audio/wav' })
response.end(request.url.endsWith('.png') ? image : soundCheckWav)
@@ -42,6 +43,8 @@ module.exports = async ({ evaluate, poll }) => {
await evaluate('window.screenBridge.onlineAction({ action: "select-game", gameId: 7 })')
const result = await evaluate('window.screenBridge.onlineState()')
assert.equal(result.materials.length, 16)
assert.ok(requests.includes('/api/scenarios/23/full'))
assert.ok(!requests.includes('/api/scenarios/23'))
assert.ok(result.materials.every((m) => m.status === 'ready'))
assert.ok(result.materials.some((m) => m.usages.some((u) => u.name === 'Фон')))
assert.ok(result.materials.some((m) => m.usages.some((u) => u.name === 'Вступление')))