Files
supabase/apps/studio/components/interfaces/Settings/Logs/Logs.utils.test.ts
Jordi EnricandClaude Sonnet 4.6 e9d951a65c feat(logs): show edge function name in logs FE-2798 (#43967)
## Problem

The edge function logs table at
`/observability/logs/edge-functions-logs` did not show which edge
function was called. Users had to open individual log entries to find
the function name, even though the information is available in the log
data.

## Fix

- Added `request.pathname` to the SQL SELECT query for
`function_edge_logs` so the pathname is returned with each row.
- Added `extractEdgeFunctionName` utility that parses the last path
segment from the pathname (e.g. `/functions/v1/hello-world-1` becomes
`hello-world-1`).
- Updated `FunctionsEdgeColumnRender` to display the function name
between the HTTP method and the log ID columns.
- Added unit tests for `extractEdgeFunctionName` covering normal paths,
null/undefined, non-string values, and pathnames without slashes.

<img width="1472" height="776" alt="CleanShot 2026-03-19 at 13 18 47@2x"
src="https://github.com/user-attachments/assets/e437f7df-7aab-4ea5-b421-95bab3923605"
/>


## How to test

- Navigate to Logs and Analytics, then Edge Functions logs.
- Confirm each row now shows the function name (e.g. `hello-world-1`)
between the HTTP method and the log ID.
- Confirm rows without a pathname (e.g. non-platform or self-hosted)
still render without errors.
- Confirm clicking a row still opens the log detail panel correctly.

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 09:39:22 +01:00

119 lines
3.8 KiB
TypeScript

import { describe, expect, test } from 'vitest'
import type { LogData } from './Logs.types'
import {
buildLogsPrompt,
extractEdgeFunctionName,
formatLogsAsJson,
formatLogsAsMarkdown,
} from './Logs.utils'
const createLog = (overrides: Partial<LogData> = {}): LogData => ({
id: 'test-id',
timestamp: 1621323232312,
event_message: 'test message',
...overrides,
})
describe('Logs.utils', () => {
describe('formatLogsAsJson', () => {
test('formats single log as JSON', () => {
const rows: LogData[] = [createLog({ id: '1', event_message: 'test message' })]
const result = formatLogsAsJson(rows)
expect(result).toContain('"id": "1"')
expect(result).toContain('"event_message": "test message"')
})
test('formats multiple logs as JSON array', () => {
const rows: LogData[] = [
createLog({ id: '1', event_message: 'first' }),
createLog({ id: '2', event_message: 'second' }),
]
const result = formatLogsAsJson(rows)
expect(result).toContain('"id": "1"')
expect(result).toContain('"id": "2"')
})
})
describe('formatLogsAsMarkdown', () => {
test('formats single log with timestamp', () => {
const rows: LogData[] = [
createLog({
id: '123',
timestamp: 1621323232312,
event_message: 'Test error',
status: '500',
}),
]
const result = formatLogsAsMarkdown(rows)
expect(result).toContain('## Log 1')
expect(result).toContain('**Timestamp:**')
expect(result).toContain('**Message:** Test error')
expect(result).toContain('**Details:**')
})
test('formats multiple logs with separators', () => {
const rows: LogData[] = [
createLog({ id: '1', event_message: 'first error' }),
createLog({ id: '2', event_message: 'second error' }),
]
const result = formatLogsAsMarkdown(rows)
expect(result).toContain('## Log 1')
expect(result).toContain('## Log 2')
expect(result).toContain('---')
})
})
describe('buildLogsPrompt', () => {
test('builds prompt with single log', () => {
const rows: LogData[] = [createLog({ id: '1', event_message: 'error occurred' })]
const result = buildLogsPrompt(rows)
expect(result).toContain('1 Supabase log entry')
expect(result).toContain('error occurred')
expect(result).toContain('What do these logs indicate')
})
test('builds prompt with multiple logs', () => {
const rows: LogData[] = [
createLog({ id: '1', event_message: 'error 1' }),
createLog({ id: '2', event_message: 'error 2' }),
]
const result = buildLogsPrompt(rows)
expect(result).toContain('2 Supabase log entries')
})
test('handles singular correctly', () => {
const rows: LogData[] = [createLog({ id: '1', event_message: 'single error' })]
const result = buildLogsPrompt(rows)
expect(result).toContain('1 Supabase log entry')
})
})
describe('extractEdgeFunctionName', () => {
test('extracts function name from full pathname', () => {
expect(extractEdgeFunctionName('/functions/v1/hello-world-1')).toBe('hello-world-1')
})
test('returns empty string for null', () => {
expect(extractEdgeFunctionName(null)).toBe('')
})
test('returns empty string for undefined', () => {
expect(extractEdgeFunctionName(undefined)).toBe('')
})
test('returns empty string for non-string values', () => {
expect(extractEdgeFunctionName(42)).toBe('')
expect(extractEdgeFunctionName({})).toBe('')
})
test('handles pathname with no slashes', () => {
expect(extractEdgeFunctionName('my-function')).toBe('my-function')
})
test('handles trailing slash', () => {
expect(extractEdgeFunctionName('/functions/v1/hello-world-1/')).toBe('hello-world-1')
})
})
})