Files
supabase/apps/studio/data/reports/v2/edge-functions.test.tsx

432 lines
13 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
aggregateInvocationsByTimestamp,
filterToWhereClause,
transformInvocationData,
} from './edge-functions.config'
import {
extractStatusCodesFromData,
generateStatusCodeAttributes,
transformStatusCodeData,
} from '@/components/interfaces/Reports/Reports.utils'
const defaultFilters = {
status_code: null,
region: [],
execution_time: null,
functions: [],
}
describe('extractStatusCodesFromData', () => {
it('should extract and sort unique status codes from the data', () => {
const data = [
{ status_code: 200 },
{ status_code: 500 },
{ status_code: 200 },
{ status_code: 404 },
]
const result = extractStatusCodesFromData(data)
expect(result).toEqual(['200', '404', '500'])
})
it('should handle an empty array', () => {
const data: any[] = []
const result = extractStatusCodesFromData(data)
expect(result).toEqual([])
})
it('should handle data with missing status_code properties', () => {
const data = [{ status_code: 200 }, {}, { status_code: 500 }]
const result = extractStatusCodesFromData(data)
expect(result).toEqual(['200', '500'])
})
it('should handle various data types for status_code', () => {
const data = [{ status_code: 200 }, { status_code: '500' }, { status_code: 404 }]
const result = extractStatusCodesFromData(data)
expect(result).toEqual(['200', '404', '500'])
})
})
describe('generateStatusCodeAttributes', () => {
it('should generate the correct attributes for a list of status codes', () => {
const statusCodes = ['200', '404', '500']
const result = generateStatusCodeAttributes(statusCodes)
expect(result.map(({ color, ...rest }) => rest)).toEqual([
{
attribute: '200',
label: '200 OK',
},
{
attribute: '404',
label: '404 Not Found',
},
{
attribute: '500',
label: '500 Internal Server Error',
},
])
})
it('should handle an empty array', () => {
const statusCodes: string[] = []
const result = generateStatusCodeAttributes(statusCodes)
expect(result).toEqual([])
})
})
describe('transformStatusCodeData', () => {
it('should pivot the data correctly', () => {
const data = [
{ timestamp: '2023-01-01T00:00:00Z', status_code: 200, count: 10 },
{ timestamp: '2023-01-01T00:00:00Z', status_code: 500, count: 5 },
{ timestamp: '2023-01-02T00:00:00Z', status_code: 200, count: 20 },
]
const result = transformStatusCodeData(data, ['200', '500'])
expect(result).toEqual([
{ timestamp: '2023-01-01T00:00:00.000Z', '200': 10, '500': 5 },
{ timestamp: '2023-01-02T00:00:00.000Z', '200': 20, '500': 0 },
])
})
it('should handle an empty array', () => {
const data: any[] = []
const result = transformStatusCodeData(data, [])
expect(result).toEqual([])
})
it('should handle a single entry', () => {
const data = [{ timestamp: '2023-01-01T00:00:00Z', status_code: 200, count: 10 }]
const result = transformStatusCodeData(data, ['200', '404'])
expect(result).toEqual([{ timestamp: '2023-01-01T00:00:00.000Z', '200': 10, '404': 0 }])
})
})
describe('transformInvocationData', () => {
const mockFunctions = [
{ id: 'func1', name: 'Function One' },
{ id: 'func2', name: 'Function Two' },
]
it('should transform raw invocation data with function names', () => {
const rawData = [
{
timestamp: '2023-01-01T00:00:00Z',
function_id: 'func1',
count: 10,
},
{
timestamp: '2023-01-01T01:00:00Z',
function_id: 'func2',
count: 5,
},
]
const result = transformInvocationData(rawData, mockFunctions)
expect(result).toEqual([
{
timestamp: '2023-01-01T00:00:00.000Z',
function_id: 'func1',
count: 10,
function_name: 'Function One',
},
{
timestamp: '2023-01-01T01:00:00.000Z',
function_id: 'func2',
count: 5,
function_name: 'Function Two',
},
])
})
it('should use function_id as fallback when function name not found', () => {
const rawData = [
{
timestamp: '2023-01-01T00:00:00Z',
function_id: 'unknown_func',
count: 10,
},
]
const result = transformInvocationData(rawData, mockFunctions)
expect(result).toEqual([
{
timestamp: '2023-01-01T00:00:00.000Z',
function_id: 'unknown_func',
count: 10,
function_name: 'unknown_func',
},
])
})
it('should handle unix micro timestamps', () => {
const rawData = [
{
timestamp: 1672531200000000, // Unix micro timestamp for 2023-01-01T00:00:00Z
function_id: 'func1',
count: 10,
},
]
const result = transformInvocationData(rawData, mockFunctions)
expect(result[0].timestamp).toBe('2023-01-01T00:00:00.000Z')
expect(result[0].function_name).toBe('Function One')
})
it('should handle empty data array', () => {
const result = transformInvocationData([], mockFunctions)
expect(result).toEqual([])
})
it('should handle empty functions array', () => {
const rawData = [
{
timestamp: '2023-01-01T00:00:00Z',
function_id: 'func1',
count: 10,
},
]
const result = transformInvocationData(rawData, [])
expect(result).toEqual([
{
timestamp: '2023-01-01T00:00:00.000Z',
function_id: 'func1',
count: 10,
function_name: 'func1',
},
])
})
})
describe('aggregateInvocationsByTimestamp', () => {
it('should aggregate counts by timestamp', () => {
const data = [
{ timestamp: '2023-01-01T00:00:00.000Z', function_id: 'func1', count: 10 },
{ timestamp: '2023-01-01T00:00:00.000Z', function_id: 'func2', count: 5 },
{ timestamp: '2023-01-01T01:00:00.000Z', function_id: 'func1', count: 20 },
]
const result = aggregateInvocationsByTimestamp(data)
expect(result).toEqual([
{ timestamp: '2023-01-01T00:00:00.000Z', count: 15 },
{ timestamp: '2023-01-01T01:00:00.000Z', count: 20 },
])
})
it('should handle single entry per timestamp', () => {
const data = [
{ timestamp: '2023-01-01T00:00:00.000Z', function_id: 'func1', count: 10 },
{ timestamp: '2023-01-01T01:00:00.000Z', function_id: 'func2', count: 5 },
]
const result = aggregateInvocationsByTimestamp(data)
expect(result).toEqual([
{ timestamp: '2023-01-01T00:00:00.000Z', count: 10 },
{ timestamp: '2023-01-01T01:00:00.000Z', count: 5 },
])
})
it('should handle empty data array', () => {
const result = aggregateInvocationsByTimestamp([])
expect(result).toEqual([])
})
it('should handle multiple entries with same timestamp and different counts', () => {
const data = [
{ timestamp: '2023-01-01T00:00:00.000Z', function_id: 'func1', count: 1 },
{ timestamp: '2023-01-01T00:00:00.000Z', function_id: 'func2', count: 2 },
{ timestamp: '2023-01-01T00:00:00.000Z', function_id: 'func3', count: 3 },
]
const result = aggregateInvocationsByTimestamp(data)
expect(result).toEqual([{ timestamp: '2023-01-01T00:00:00.000Z', count: 6 }])
})
it('should preserve timestamp order from reduce operation', () => {
const data = [
{ timestamp: '2023-01-01T02:00:00.000Z', function_id: 'func1', count: 30 },
{ timestamp: '2023-01-01T00:00:00.000Z', function_id: 'func1', count: 10 },
{ timestamp: '2023-01-01T01:00:00.000Z', function_id: 'func1', count: 20 },
]
const result = aggregateInvocationsByTimestamp(data)
expect(result).toHaveLength(3)
expect(result.map((item) => item.timestamp)).toEqual([
'2023-01-01T02:00:00.000Z',
'2023-01-01T00:00:00.000Z',
'2023-01-01T01:00:00.000Z',
])
})
})
describe('filterToWhereClause', () => {
it('should return empty string when no filters are provided', () => {
const result = filterToWhereClause()
expect(result).toBe('')
})
it('should return empty string when filters object is empty', () => {
const result = filterToWhereClause(defaultFilters)
expect(result).toBe('')
})
it('should generate WHERE clause for functions filter', () => {
const filters = {
...defaultFilters,
functions: ['func1', 'func2'],
}
const result = filterToWhereClause(filters)
expect(result).toBe("WHERE function_id IN ('func1','func2')")
})
it('should generate WHERE clause for status_code filter', () => {
const filters = {
...defaultFilters,
status_code: {
operator: '>=' as const,
value: 400,
},
}
const result = filterToWhereClause(filters)
expect(result).toBe('WHERE response.status_code >= 400')
})
it('should generate WHERE clause for region filter', () => {
const filters = {
...defaultFilters,
region: ['us-east-1', 'eu-west-1'],
}
const result = filterToWhereClause(filters)
expect(result).toBe("WHERE h.x_sb_edge_region IN ('us-east-1','eu-west-1')")
})
it('should generate WHERE clause for execution_time filter', () => {
const filters = {
...defaultFilters,
execution_time: {
operator: '<' as const,
value: 1000,
},
}
const result = filterToWhereClause(filters)
expect(result).toBe('WHERE m.execution_time_ms < 1000')
})
it('should combine multiple filters with AND', () => {
const filters = {
functions: ['func1'],
status_code: {
operator: '=' as const,
value: 200,
},
region: ['us-east-1'],
execution_time: {
operator: '<=' as const,
value: 500,
},
}
const result = filterToWhereClause(filters)
expect(result).toBe(
"WHERE function_id IN ('func1') AND response.status_code = 200 AND h.x_sb_edge_region IN ('us-east-1') AND m.execution_time_ms <= 500"
)
})
it('should handle functions filter with no selected functions', () => {
const filters = {
...defaultFilters,
functions: [],
}
const result = filterToWhereClause(filters)
expect(result).toBe('')
})
it('should handle region filter with no selected regions', () => {
const filters = {
...defaultFilters,
region: [],
}
const result = filterToWhereClause(filters)
expect(result).toBe('')
})
it('should handle single function selection', () => {
const filters = {
...defaultFilters,
functions: ['single-func'],
}
const result = filterToWhereClause(filters)
expect(result).toBe("WHERE function_id IN ('single-func')")
})
it('should handle single region selection', () => {
const filters = {
...defaultFilters,
region: ['single-region'],
}
const result = filterToWhereClause(filters)
expect(result).toBe("WHERE h.x_sb_edge_region IN ('single-region')")
})
it('should handle all comparison operators for status_code', () => {
const operators = ['=', '!=', '>', '>=', '<', '<='] as const
operators.forEach((operator) => {
const filters = {
...defaultFilters,
status_code: {
operator,
value: 200,
},
}
const result = filterToWhereClause(filters)
expect(result).toBe(`WHERE response.status_code ${operator} 200`)
})
})
it('should handle all comparison operators for execution_time', () => {
const operators = ['=', '!=', '>', '>=', '<', '<='] as const
operators.forEach((operator) => {
const filters = {
...defaultFilters,
execution_time: {
operator,
value: 100,
},
}
const result = filterToWhereClause(filters)
expect(result).toBe(`WHERE m.execution_time_ms ${operator} 100`)
})
})
it('should handle numeric values correctly', () => {
const filters = {
...defaultFilters,
status_code: {
operator: '>' as const,
value: 0,
},
execution_time: {
operator: '>=' as const,
value: 1.5,
},
}
const result = filterToWhereClause(filters)
expect(result).toBe('WHERE response.status_code > 0 AND m.execution_time_ms >= 1.5')
})
it('should handle special characters in function IDs and regions', () => {
const filters = {
...defaultFilters,
functions: ['func-with-dash', 'func_with_underscore', 'func.with.dots'],
region: ['region-with-dash', 'region_with_underscore'],
}
const result = filterToWhereClause(filters)
expect(result).toBe(
"WHERE function_id IN ('func-with-dash','func_with_underscore','func.with.dots') AND h.x_sb_edge_region IN ('region-with-dash','region_with_underscore')"
)
})
})