Files
supabase/apps/studio/components/ui/AIAssistantPanel/MessagePartQueryLogs.test.tsx
Saxon Fletcher ee6961beb0 fix(studio): clarify assistant tool terminal states (#49364)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Small Assistant terminal-state fixes.

## Stack context

This stack is based on #49352 (`chore/assistant-tool-outcomes`) and
assumes #49350–#49352 merge first.

Review bottom to top:

1. #49361 — assistant notebook run tool
2. #49362 — assistant notebook run UI
3. #49364 — terminal-state polish

## What is the current behavior?

Completed notebook updates can be re-diffed against newer live content,
and log-query failures can use SQL-specific or empty fallback UI.

## What is the new behavior?

- Replaces completed notebook update previews with a stable
success/error/skipped summary.
- Keeps the Open notebook action on successful updates.
- Uses logs-specific failure copy for Assistant log queries.
- Shows an explicit fallback when a failed log tool input or output
cannot be parsed.

## How to test manually

### Notebook update terminal states

1. Ask the AI Assistant to update an existing notebook, then approve the
proposal.
2. Confirm the proposal becomes a compact **Notebook updated: [name]**
summary instead of re-diffing against the newly saved notebook.
3. Click **Open notebook** and confirm it opens the updated notebook.
4. Request another notebook update and click **Skip**. Confirm the
terminal summary says **Skipped notebook update**.
5. Refresh or reopen the conversation and confirm both summaries remain
stable.

### Logs failure copy

1. Ask the Assistant to query Logs with an intentionally invalid table
or column and approve the query.
2. Confirm the failed result says **Failed to query logs**, not **Failed
to execute SQL**.
3. Confirm the failed tool remains visible rather than disappearing when
its result cannot be rendered.

## Automated test

The focused top-of-stack suite passes 9 test files and 85 tests.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Added clearer failure messaging when query logs contain invalid input,
missing results, or errors.
- Added compact summaries for completed, failed, and skipped notebook
updates.
- Notebook update summaries include an “Open notebook” link when
applicable.

- **Bug Fixes**
- Improved handling and display of query-log failures instead of showing
blank content.
- Preserved detailed previews for notebook updates that are still in
progress.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-25 18:16:34 +10:00

89 lines
2.6 KiB
TypeScript

import { screen } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
import { MessageProvider } from './Message.Context'
import { MessagePartQueryLogs } from './MessagePartQueryLogs'
import { customRender as render } from '@/tests/lib/custom-render'
vi.mock('@/components/interfaces/Explorer/QueryEditor', () => ({
QueryEditor: ({
query,
result,
}: {
query: { uncheckedSql: string }
result?: { error?: { message?: string } }
}) => (
<div data-testid="query-editor">
<span>{query.uncheckedSql}</span>
<span>{result?.error?.message}</span>
</div>
),
}))
vi.mock('@/lib/telemetry/track', () => ({ useTrack: () => vi.fn() }))
vi.mock('@/state/role-impersonation-state', () => ({
useLocalRoleImpersonationState: () => ({}),
}))
type QueryLogsToolPart = Parameters<typeof MessagePartQueryLogs>[0]['toolPart']
const messageInfo = {
id: 'message-1',
isLoading: false,
state: 'idle' as const,
}
const messageActions = {
onDelete: vi.fn(),
onEdit: vi.fn(),
onBranch: vi.fn(),
onCancelEdit: vi.fn(),
}
function renderPart(toolPart: Parameters<typeof MessagePartQueryLogs>[0]['toolPart']) {
return render(
<MessageProvider messageInfo={messageInfo} messageActions={messageActions}>
<MessagePartQueryLogs toolPart={toolPart} />
</MessageProvider>
)
}
describe('MessagePartQueryLogs', () => {
it('renders an output error using raw input when submitted input is unavailable', () => {
renderPart({
toolCallId: 'query-logs-1',
state: 'output-error',
input: undefined,
rawInput: { sql: 'select count(*) from edge_logs' },
errorText: 'Log query timed out',
} as QueryLogsToolPart)
expect(screen.getByText('select count(*) from edge_logs')).toBeInTheDocument()
expect(screen.getByText('Log query timed out')).toBeInTheDocument()
})
it('uses logs-specific copy for a rendered tool error', () => {
renderPart({
toolCallId: 'logs-1',
state: 'output-error',
input: { sql: 'select event_message from edge_logs' },
output: undefined,
errorText: 'Analytics request failed',
})
expect(screen.getByText('Failed to query logs')).toBeInTheDocument()
expect(screen.queryByText('Failed to execute SQL')).not.toBeInTheDocument()
})
it('shows an explicit failure when errored input cannot be parsed', () => {
renderPart({
toolCallId: 'logs-2',
state: 'output-error',
input: { invalid: true },
output: undefined,
errorText: 'Analytics request failed',
})
expect(screen.getByText('Failed to query logs.')).toBeInTheDocument()
})
})