Files
supabase/apps/studio/lib/api/apiHelpers.test.ts

260 lines
7.6 KiB
TypeScript

import type { IncomingHttpHeaders } from 'node:http'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import {
commaSeparatedStringIntoArray,
constructHeaders,
fromNodeHeaders,
toSnakeCase,
zBooleanString,
} from './apiHelpers'
vi.mock('@/lib/constants', () => ({
IS_PLATFORM: false,
}))
describe('apiHelpers', () => {
describe('constructHeaders', () => {
beforeEach(() => {
process.env.SUPABASE_SERVICE_KEY = 'test-service-key'
})
it('should return default headers when no headers are provided', () => {
const result = constructHeaders(null as any)
expect(result).toEqual({
'Content-Type': 'application/json',
Accept: 'application/json',
})
})
it('should clean and include only allowed headers', () => {
const inputHeaders = {
Accept: 'application/json',
Authorization: 'Bearer token',
'Content-Type': 'application/json',
'x-connection-encrypted': 'true',
cookie: 'test-cookie',
'User-Agent': 'test-agent',
Referer: 'test-referer',
}
const result = constructHeaders(inputHeaders)
expect(result).toEqual({
Accept: 'application/json',
Authorization: 'Bearer token',
'Content-Type': 'application/json',
'x-connection-encrypted': 'true',
cookie: 'test-cookie',
apiKey: 'test-service-key',
})
})
it('should remove undefined values from headers', () => {
const inputHeaders = {
Accept: undefined,
Authorization: 'Bearer token',
'Content-Type': 'application/json',
cookie: undefined,
}
const result = constructHeaders(inputHeaders)
expect(result).toEqual({
Authorization: 'Bearer token',
'Content-Type': 'application/json',
apiKey: 'test-service-key',
})
})
})
describe('toSnakeCase', () => {
it('should return null for null input', () => {
expect(toSnakeCase(null)).toBeNull()
})
it('should convert object keys to snake case', () => {
const input = {
firstName: 'John',
lastName: 'Doe',
contactInfo: {
emailAddress: 'john@example.com',
phoneNumber: '1234567890',
},
}
const expected = {
first_name: 'John',
last_name: 'Doe',
contact_info: {
email_address: 'john@example.com',
phone_number: '1234567890',
},
}
expect(toSnakeCase(input)).toEqual(expected)
})
it('should handle arrays of objects', () => {
const input = [
{ firstName: 'John', lastName: 'Doe' },
{ firstName: 'Jane', lastName: 'Smith' },
]
const expected = [
{ first_name: 'John', last_name: 'Doe' },
{ first_name: 'Jane', last_name: 'Smith' },
]
expect(toSnakeCase(input)).toEqual(expected)
})
it('should handle arrays of primitive values', () => {
const input = [1, 'test', true]
expect(toSnakeCase(input)).toEqual([1, 'test', true])
})
it('should handle nested arrays', () => {
const input = {
users: [
{ firstName: 'John', contactInfo: { emailAddress: 'john@example.com' } },
{ firstName: 'Jane', contactInfo: { emailAddress: 'jane@example.com' } },
],
}
const expected = {
users: [
{ first_name: 'John', contact_info: { email_address: 'john@example.com' } },
{ first_name: 'Jane', contact_info: { email_address: 'jane@example.com' } },
],
}
expect(toSnakeCase(input)).toEqual(expected)
})
it('should handle primitive values', () => {
expect(toSnakeCase('test')).toBe('test')
expect(toSnakeCase(123)).toBe(123)
expect(toSnakeCase(true)).toBe(true)
})
})
describe('zBooleanString', () => {
it('should transform "true" string to boolean true', () => {
const schema = zBooleanString()
const result = schema.parse('true')
expect(result).toBe(true)
})
it('should transform "false" string to boolean false', () => {
const schema = zBooleanString()
const result = schema.parse('false')
expect(result).toBe(false)
})
it('should throw error for invalid boolean string', () => {
const schema = zBooleanString()
expect(() => schema.parse('invalid')).toThrow('must be a boolean string')
})
it('should throw custom error message when provided', () => {
const customError = 'Custom boolean error'
const schema = zBooleanString(customError)
expect(() => schema.parse('invalid')).toThrow(customError)
})
it('should throw error for empty string', () => {
const schema = zBooleanString()
expect(() => schema.parse('')).toThrow('must be a boolean string')
})
it('should throw error for non-string input', () => {
const schema = zBooleanString()
expect(() => schema.parse(true)).toThrow()
expect(() => schema.parse(false)).toThrow()
expect(() => schema.parse(123)).toThrow()
})
})
describe('commaSeparatedStringIntoArray', () => {
it('should split comma-separated string into array', () => {
const result = commaSeparatedStringIntoArray('a,b,c')
expect(result).toEqual(['a', 'b', 'c'])
})
it('should trim whitespace from values', () => {
const result = commaSeparatedStringIntoArray('a, b , c')
expect(result).toEqual(['a', 'b', 'c'])
})
it('should filter out empty values', () => {
const result = commaSeparatedStringIntoArray('a,,b,')
expect(result).toEqual(['a', 'b'])
})
it('should handle single value', () => {
const result = commaSeparatedStringIntoArray('single')
expect(result).toEqual(['single'])
})
it('should handle empty string', () => {
const result = commaSeparatedStringIntoArray('')
expect(result).toEqual([])
})
it('should handle string with only commas', () => {
const result = commaSeparatedStringIntoArray(',,,')
expect(result).toEqual([])
})
})
describe('fromNodeHeaders', () => {
it('should convert simple node headers to fetch headers', () => {
const nodeHeaders: IncomingHttpHeaders = {
'content-type': 'application/json',
authorization: 'Bearer token',
}
const result = fromNodeHeaders(nodeHeaders)
expect(result.get('content-type')).toBe('application/json')
expect(result.get('authorization')).toBe('Bearer token')
})
it('should skip undefined values', () => {
const nodeHeaders: IncomingHttpHeaders = {
'content-type': 'application/json',
authorization: undefined,
accept: 'application/json',
}
const result = fromNodeHeaders(nodeHeaders)
expect(result.get('content-type')).toBe('application/json')
expect(result.get('authorization')).toBeNull()
expect(result.get('accept')).toBe('application/json')
})
it('should handle empty headers object', () => {
const nodeHeaders: IncomingHttpHeaders = {}
const result = fromNodeHeaders(nodeHeaders)
expect(Array.from(result.keys())).toEqual([])
})
it('should handle mixed array and string values', () => {
const nodeHeaders: IncomingHttpHeaders = {
'content-type': 'application/json',
'x-custom': ['value1', 'value2'],
authorization: 'Bearer token',
'x-empty': undefined,
}
const result = fromNodeHeaders(nodeHeaders)
expect(result.get('content-type')).toBe('application/json')
expect(result.get('authorization')).toBe('Bearer token')
expect(result.get('x-empty')).toBeNull()
expect(result.get('x-custom')).toBe('value1, value2')
})
})
})