mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
test: remove 29 low-value notification and diagnostic cases (#34703)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -5,46 +5,6 @@ const DEFAULT_MARGIN_INCHES = 1 / 2.54
|
||||
const TTL_MS = 5 * 60 * 1000
|
||||
|
||||
describe('buildPrintToPdfOptions', () => {
|
||||
it('returns empty options for empty params', () => {
|
||||
expect(buildPrintToPdfOptions({})).toEqual({})
|
||||
})
|
||||
|
||||
it('maps every well-formed field', () => {
|
||||
expect(
|
||||
buildPrintToPdfOptions({
|
||||
landscape: true,
|
||||
displayHeaderFooter: true,
|
||||
printBackground: true,
|
||||
preferCSSPageSize: true,
|
||||
generateTaggedPDF: true,
|
||||
generateDocumentOutline: false,
|
||||
scale: 1.5,
|
||||
paperWidth: 8.5,
|
||||
paperHeight: 11,
|
||||
marginTop: 0.25,
|
||||
marginBottom: 0.5,
|
||||
marginLeft: 0.75,
|
||||
marginRight: 1,
|
||||
pageRanges: '1-2',
|
||||
headerTemplate: '<span></span>',
|
||||
footerTemplate: '<span></span>'
|
||||
})
|
||||
).toEqual({
|
||||
landscape: true,
|
||||
displayHeaderFooter: true,
|
||||
printBackground: true,
|
||||
preferCSSPageSize: true,
|
||||
generateTaggedPDF: true,
|
||||
generateDocumentOutline: false,
|
||||
scale: 1.5,
|
||||
pageSize: { width: 8.5, height: 11 },
|
||||
margins: { top: 0.25, bottom: 0.5, left: 0.75, right: 1 },
|
||||
pageRanges: '1-2',
|
||||
headerTemplate: '<span></span>',
|
||||
footerTemplate: '<span></span>'
|
||||
})
|
||||
})
|
||||
|
||||
it('defaults omitted margin sides to 1cm when any side is set', () => {
|
||||
expect(buildPrintToPdfOptions({ marginTop: 0.25 }).margins).toEqual({
|
||||
top: 0.25,
|
||||
@@ -53,26 +13,6 @@ describe('buildPrintToPdfOptions', () => {
|
||||
right: DEFAULT_MARGIN_INCHES
|
||||
})
|
||||
})
|
||||
|
||||
it('drops non-finite and out-of-range numeric options', () => {
|
||||
const options = buildPrintToPdfOptions({
|
||||
scale: 0,
|
||||
paperWidth: Number.POSITIVE_INFINITY,
|
||||
paperHeight: 11,
|
||||
marginTop: Number.NaN
|
||||
})
|
||||
expect(options.scale).toBeUndefined()
|
||||
expect(options.pageSize).toBeUndefined()
|
||||
expect(options.margins).toBeUndefined()
|
||||
})
|
||||
|
||||
it('requires both paper dimensions for pageSize', () => {
|
||||
expect(buildPrintToPdfOptions({ paperWidth: 8.5 }).pageSize).toBeUndefined()
|
||||
})
|
||||
|
||||
it('ignores wrong-typed options', () => {
|
||||
expect(buildPrintToPdfOptions({ landscape: 'yes', pageRanges: 7 })).toEqual({})
|
||||
})
|
||||
})
|
||||
|
||||
describe('CdpPdfStreamStore', () => {
|
||||
@@ -91,11 +31,6 @@ describe('CdpPdfStreamStore', () => {
|
||||
expect(store.ownsHandle({})).toBe(false)
|
||||
})
|
||||
|
||||
it('mints distinct handles per stream', () => {
|
||||
const store = new CdpPdfStreamStore()
|
||||
expect(store.create(Buffer.from('a'))).not.toBe(store.create(Buffer.from('b')))
|
||||
})
|
||||
|
||||
it('reads sequential chunks and reports eof', () => {
|
||||
const store = new CdpPdfStreamStore()
|
||||
const handle = store.create(Buffer.from('abcdef'))
|
||||
@@ -127,28 +62,6 @@ describe('CdpPdfStreamStore', () => {
|
||||
expect(store.read({ handle, offset: 99 })).toEqual({ data: '', eof: true })
|
||||
})
|
||||
|
||||
it('returns null for unknown handles', () => {
|
||||
const store = new CdpPdfStreamStore()
|
||||
expect(store.read({ handle: 'nope' })).toBeNull()
|
||||
expect(store.read({})).toBeNull()
|
||||
})
|
||||
|
||||
it('drops a stream on close', () => {
|
||||
const store = new CdpPdfStreamStore()
|
||||
const handle = store.create(Buffer.from('abc'))
|
||||
|
||||
store.close({ handle })
|
||||
expect(store.read({ handle })).toBeNull()
|
||||
})
|
||||
|
||||
it('drops all streams on clear', () => {
|
||||
const store = new CdpPdfStreamStore()
|
||||
const handle = store.create(Buffer.from('abc'))
|
||||
|
||||
store.clear()
|
||||
expect(store.read({ handle })).toBeNull()
|
||||
})
|
||||
|
||||
it('evicts an abandoned stream after the TTL', () => {
|
||||
vi.useFakeTimers()
|
||||
const store = new CdpPdfStreamStore()
|
||||
|
||||
@@ -1,72 +1,13 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
classifyPRRefreshError,
|
||||
safePRRefreshErrorMessage
|
||||
} from './pr-refresh-error-classification'
|
||||
import { classifyPRRefreshError } from './pr-refresh-error-classification'
|
||||
|
||||
describe('classifyPRRefreshError', () => {
|
||||
it('classifies an HTTP 429 as rate_limited even without a rate-limit body', () => {
|
||||
expect(classifyPRRefreshError(new Error('HTTP 429 Too Many Requests'))).toBe('rate_limited')
|
||||
})
|
||||
|
||||
it('classifies secondary rate limit markers as rate_limited, not permission', () => {
|
||||
for (const message of [
|
||||
'You have exceeded a secondary rate limit',
|
||||
'abuse detection mechanism triggered',
|
||||
'abuse-rate-limits',
|
||||
'you have triggered an abuse detection mechanism'
|
||||
]) {
|
||||
expect(classifyPRRefreshError(new Error(message))).toBe('rate_limited')
|
||||
}
|
||||
})
|
||||
|
||||
it('classifies a 403 carrying Retry-After as rate_limited, not permission', () => {
|
||||
expect(classifyPRRefreshError(new Error('HTTP 403 Forbidden; Retry-After: 60'))).toBe(
|
||||
'rate_limited'
|
||||
)
|
||||
})
|
||||
|
||||
it('classifies the primary breaker language as rate_limited', () => {
|
||||
expect(classifyPRRefreshError(new Error('API rate limit exceeded for user'))).toBe(
|
||||
'rate_limited'
|
||||
)
|
||||
})
|
||||
|
||||
it('classifies a plain 403 resource denial as permission', () => {
|
||||
expect(
|
||||
classifyPRRefreshError(new Error('HTTP 403: Resource not accessible by integration'))
|
||||
).toBe('permission')
|
||||
})
|
||||
|
||||
it('classifies network failures', () => {
|
||||
for (const message of ['ETIMEDOUT', 'could not resolve host github.com', 'network is down']) {
|
||||
expect(classifyPRRefreshError(new Error(message))).toBe('network')
|
||||
}
|
||||
})
|
||||
|
||||
it('classifies 404 / could not resolve repository as repo_unavailable', () => {
|
||||
expect(classifyPRRefreshError(new Error('HTTP 404 Not Found'))).toBe('repo_unavailable')
|
||||
expect(
|
||||
classifyPRRefreshError(new Error('Could not resolve to a Repository with the name'))
|
||||
).toBe('repo_unavailable')
|
||||
})
|
||||
|
||||
it('classifies an ENOENT spawn failure as gh_unavailable', () => {
|
||||
const err = Object.assign(new Error('spawn gh ENOENT'), { code: 'ENOENT' })
|
||||
expect(classifyPRRefreshError(err)).toBe('gh_unavailable')
|
||||
expect(classifyPRRefreshError(new Error("'gh' is not recognized as an internal command"))).toBe(
|
||||
'gh_unavailable'
|
||||
)
|
||||
})
|
||||
|
||||
it('classifies auth failures after rate-limit and permission checks', () => {
|
||||
expect(classifyPRRefreshError(new Error('authentication failed: bad credentials'))).toBe('auth')
|
||||
})
|
||||
|
||||
it('falls back to unknown', () => {
|
||||
expect(classifyPRRefreshError(new Error('something unexpected happened'))).toBe('unknown')
|
||||
})
|
||||
|
||||
it('does not classify a message that merely contains "author" as auth', () => {
|
||||
for (const message of [
|
||||
'PR author octocat has no write access to the fork',
|
||||
@@ -91,10 +32,6 @@ describe('classifyPRRefreshError', () => {
|
||||
).toBe('repo_unavailable')
|
||||
})
|
||||
|
||||
it('classifies a 401 as auth', () => {
|
||||
expect(classifyPRRefreshError(new Error('HTTP 401 Unauthorized'))).toBe('auth')
|
||||
})
|
||||
|
||||
it('classifies from the real runner error shape where the signal is on .stderr', () => {
|
||||
// The runner stores diagnostics on stderr even when message is only a generic exit error.
|
||||
const permission = Object.assign(new Error('gh exited with 1.'), {
|
||||
@@ -119,21 +56,3 @@ describe('classifyPRRefreshError', () => {
|
||||
expect(classifyPRRefreshError(auth)).toBe('auth')
|
||||
})
|
||||
})
|
||||
|
||||
describe('safePRRefreshErrorMessage', () => {
|
||||
it('returns non-empty copy for every classified type without leaking raw errors', () => {
|
||||
for (const type of [
|
||||
'rate_limited',
|
||||
'auth',
|
||||
'network',
|
||||
'permission',
|
||||
'repo_unavailable',
|
||||
'gh_unavailable',
|
||||
'unknown'
|
||||
] as const) {
|
||||
const message = safePRRefreshErrorMessage(type)
|
||||
expect(message.length).toBeGreaterThan(0)
|
||||
expect(message).not.toContain('ENOENT')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -43,12 +43,4 @@ describe('buildNotificationOptions localization', () => {
|
||||
body: 'Un agente de programación terminó de trabajar.'
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps the English copy for headless and initialized English translators', () => {
|
||||
for (const translate of [undefined, translateMain]) {
|
||||
expect(buildNotificationOptions(BELL, translate).title).toBe('Bell in feature-x')
|
||||
expect(buildNotificationOptions(TOOL, translate).body).toBe('Using Bash: ls {{tool}}')
|
||||
expect(buildNotificationOptions(FALLBACK, translate).title).toBe('Task complete in workspace')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,7 +3,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
getDispatchHandler,
|
||||
notificationCtorMock,
|
||||
notificationShowMock,
|
||||
resetNotificationDispatchMocks
|
||||
} from './notifications-test-harness'
|
||||
|
||||
@@ -32,106 +31,6 @@ describe('registerNotificationHandlers', () => {
|
||||
resetNotificationDispatchMocks()
|
||||
})
|
||||
|
||||
it('delivers a notification when the event is allowed', async () => {
|
||||
registerNotificationHandlers({
|
||||
getSettings: () => ({
|
||||
notifications: {
|
||||
enabled: true,
|
||||
agentTaskComplete: true,
|
||||
terminalBell: false,
|
||||
suppressWhenFocused: true
|
||||
}
|
||||
})
|
||||
} as never)
|
||||
|
||||
const handler = getDispatchHandler()
|
||||
expect(
|
||||
await handler(
|
||||
{},
|
||||
{ source: 'agent-task-complete', repoLabel: 'orca', worktreeLabel: 'feat/notis' }
|
||||
)
|
||||
).toEqual({ delivered: true })
|
||||
expect(notificationCtorMock).toHaveBeenCalledWith({
|
||||
title: 'Task complete in feat/notis',
|
||||
body: 'orca'
|
||||
})
|
||||
expect(notificationShowMock).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('formats agent-task-complete with the agent response when a status snapshot is present', async () => {
|
||||
registerNotificationHandlers({
|
||||
getSettings: () => ({
|
||||
notifications: {
|
||||
enabled: true,
|
||||
agentTaskComplete: true,
|
||||
terminalBell: false,
|
||||
suppressWhenFocused: true
|
||||
}
|
||||
})
|
||||
} as never)
|
||||
|
||||
const handler = getDispatchHandler()
|
||||
expect(
|
||||
await handler(
|
||||
{},
|
||||
{
|
||||
source: 'agent-task-complete',
|
||||
worktreeId: 'repo::wt1',
|
||||
worktreeLabel: 'feat/notis',
|
||||
repoLabel: 'orca',
|
||||
terminalTitle: '* Claude done',
|
||||
agentType: 'codex',
|
||||
agentState: 'done',
|
||||
agentPrompt: 'Fix rich notification text',
|
||||
agentLastAssistantMessage: 'Updated the notification body.'
|
||||
}
|
||||
)
|
||||
).toEqual({ delivered: true })
|
||||
|
||||
expect(notificationCtorMock).toHaveBeenCalledWith({
|
||||
title: 'orca / feat/notis - Codex finished',
|
||||
body: 'Updated the notification body.'
|
||||
})
|
||||
})
|
||||
|
||||
it.each([true, false, undefined])(
|
||||
'includes the repo name regardless of the legacy multiple-repo flag (%s)',
|
||||
async (hasMultipleActiveRepos) => {
|
||||
registerNotificationHandlers({
|
||||
getSettings: () => ({
|
||||
notifications: {
|
||||
enabled: true,
|
||||
agentTaskComplete: true,
|
||||
terminalBell: false,
|
||||
suppressWhenFocused: true
|
||||
}
|
||||
})
|
||||
} as never)
|
||||
|
||||
const handler = getDispatchHandler()
|
||||
expect(
|
||||
await handler(
|
||||
{},
|
||||
{
|
||||
source: 'agent-task-complete',
|
||||
worktreeId: 'repo::wt1',
|
||||
worktreeLabel: 'feat/notis',
|
||||
repoLabel: 'orca',
|
||||
hasMultipleActiveRepos,
|
||||
agentType: 'codex',
|
||||
agentState: 'done',
|
||||
agentLastAssistantMessage: 'Updated the notification body.'
|
||||
}
|
||||
)
|
||||
).toEqual({ delivered: true })
|
||||
|
||||
expect(notificationCtorMock).toHaveBeenCalledWith({
|
||||
title: 'orca / feat/notis - Codex finished',
|
||||
body: 'Updated the notification body.'
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
it('keeps a readable body when no assistant response was captured', async () => {
|
||||
registerNotificationHandlers({
|
||||
getSettings: () => ({
|
||||
@@ -167,38 +66,6 @@ describe('registerNotificationHandlers', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ agentType: 'opencode2', label: 'OpenCode 2' },
|
||||
{ agentType: 'copilot', label: 'GitHub Copilot' }
|
||||
])('names $agentType the way the app does', async ({ agentType, label }) => {
|
||||
registerNotificationHandlers({
|
||||
getSettings: () => ({
|
||||
notifications: {
|
||||
enabled: true,
|
||||
agentTaskComplete: true,
|
||||
terminalBell: false,
|
||||
suppressWhenFocused: true
|
||||
}
|
||||
})
|
||||
} as never)
|
||||
|
||||
await getDispatchHandler()(
|
||||
{},
|
||||
{
|
||||
source: 'agent-task-complete',
|
||||
worktreeId: 'repo::wt1',
|
||||
worktreeLabel: 'feat/notis',
|
||||
agentType,
|
||||
agentState: 'done'
|
||||
}
|
||||
)
|
||||
|
||||
expect(notificationCtorMock).toHaveBeenCalledWith({
|
||||
title: `feat/notis - ${label} finished`,
|
||||
body: `${label} finished.`
|
||||
})
|
||||
})
|
||||
|
||||
it('formats blocked and interrupted agent snapshots distinctly', async () => {
|
||||
registerNotificationHandlers({
|
||||
getSettings: () => ({
|
||||
@@ -292,137 +159,4 @@ describe('registerNotificationHandlers', () => {
|
||||
expect(options.body).not.toContain('\n')
|
||||
expect(options.body.length).toBeLessThanOrEqual(180)
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ agentState: 'working', expected: 'feat/notis - Claude working' },
|
||||
{ agentState: 'blocked', expected: 'feat/notis - Claude needs input' },
|
||||
{ agentState: 'waiting', expected: 'feat/notis - Claude needs input' },
|
||||
{ agentState: 'done', expected: 'feat/notis - Claude finished' },
|
||||
{ agentState: undefined, expected: 'feat/notis - Claude finished' }
|
||||
])('titles agentState $agentState without claiming a false finish', async (scenario) => {
|
||||
registerNotificationHandlers({
|
||||
getSettings: () => ({
|
||||
notifications: {
|
||||
enabled: true,
|
||||
agentTaskComplete: true,
|
||||
terminalBell: false,
|
||||
suppressWhenFocused: true
|
||||
}
|
||||
})
|
||||
} as never)
|
||||
|
||||
const handler = getDispatchHandler()
|
||||
await handler(
|
||||
{},
|
||||
{
|
||||
source: 'agent-task-complete',
|
||||
worktreeLabel: 'feat/notis',
|
||||
agentType: 'claude',
|
||||
...(scenario.agentState ? { agentState: scenario.agentState } : {}),
|
||||
agentLastAssistantMessage: 'Ran the suite.'
|
||||
}
|
||||
)
|
||||
|
||||
expect(notificationCtorMock).toHaveBeenCalledWith({
|
||||
title: scenario.expected,
|
||||
body: 'Ran the suite.'
|
||||
})
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ agentTurnOutcome: 'cancellation', word: 'stopped' },
|
||||
{ agentTurnOutcome: 'failure', word: 'failed' },
|
||||
// A turn cut short by anything but the user is a fault, worded as one.
|
||||
{ agentTurnOutcome: 'interruption', word: 'failed' },
|
||||
{ agentTurnOutcome: 'unconfirmed', word: 'stopped' },
|
||||
{ agentTurnOutcome: 'superseded', word: 'stopped' },
|
||||
{ agentTurnOutcome: 'success', word: 'finished' },
|
||||
{ agentTurnOutcome: undefined, word: 'finished' }
|
||||
] as const)('words a $agentTurnOutcome finish as $word', async ({ agentTurnOutcome, word }) => {
|
||||
registerNotificationHandlers({
|
||||
getSettings: () => ({
|
||||
notifications: {
|
||||
enabled: true,
|
||||
agentTaskComplete: true,
|
||||
terminalBell: false,
|
||||
suppressWhenFocused: true
|
||||
}
|
||||
})
|
||||
} as never)
|
||||
|
||||
const handler = getDispatchHandler()
|
||||
await handler(
|
||||
{},
|
||||
{
|
||||
source: 'agent-task-complete',
|
||||
worktreeLabel: 'feat/notis',
|
||||
agentType: 'claude',
|
||||
agentState: 'done',
|
||||
...(agentTurnOutcome ? { agentTurnOutcome } : {})
|
||||
}
|
||||
)
|
||||
|
||||
expect(notificationCtorMock).toHaveBeenCalledWith({
|
||||
title: `feat/notis - Claude ${word}`,
|
||||
body: `Claude ${word}.`
|
||||
})
|
||||
})
|
||||
|
||||
it('counts a success verdict alone as an agent snapshot', async () => {
|
||||
registerNotificationHandlers({
|
||||
getSettings: () => ({
|
||||
notifications: {
|
||||
enabled: true,
|
||||
agentTaskComplete: true,
|
||||
terminalBell: false,
|
||||
suppressWhenFocused: true
|
||||
}
|
||||
})
|
||||
} as never)
|
||||
|
||||
const handler = getDispatchHandler()
|
||||
await handler(
|
||||
{},
|
||||
{ source: 'agent-task-complete', worktreeLabel: 'feat/notis', agentTurnOutcome: 'success' }
|
||||
)
|
||||
|
||||
expect(notificationCtorMock).toHaveBeenCalledWith({
|
||||
title: 'feat/notis - Agent finished',
|
||||
body: 'Agent finished.'
|
||||
})
|
||||
})
|
||||
|
||||
it('uses tool context before falling back when no prompt or assistant preview exists', async () => {
|
||||
registerNotificationHandlers({
|
||||
getSettings: () => ({
|
||||
notifications: {
|
||||
enabled: true,
|
||||
agentTaskComplete: true,
|
||||
terminalBell: false,
|
||||
suppressWhenFocused: true
|
||||
}
|
||||
})
|
||||
} as never)
|
||||
|
||||
const handler = getDispatchHandler()
|
||||
expect(
|
||||
await handler(
|
||||
{},
|
||||
{
|
||||
source: 'agent-task-complete',
|
||||
worktreeId: 'repo::wt1',
|
||||
worktreeLabel: 'feat/notis',
|
||||
agentType: 'unknown',
|
||||
agentState: 'working',
|
||||
agentToolName: 'Bash',
|
||||
agentToolInput: 'pnpm test'
|
||||
}
|
||||
)
|
||||
).toEqual({ delivered: true })
|
||||
|
||||
expect(notificationCtorMock).toHaveBeenCalledWith({
|
||||
title: 'feat/notis - Agent working',
|
||||
body: 'Using Bash: pnpm test'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user