mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
432 lines
13 KiB
TypeScript
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')"
|
|
)
|
|
})
|
|
})
|