sangwaritaxi_website/node_modules/@jsr/supabase__supabase-js/test/integration.test.ts

361 lines
11 KiB
TypeScript
Executable File

import { createClient, RealtimeChannel, SupabaseClient } from '../src/index.ts'
// These tests assume that a local Supabase server is already running
// Start a local Supabase instance with 'supabase start' before running these tests
// Default local dev credentials from Supabase CLI
const SUPABASE_URL = 'http://127.0.0.1:54321'
const ANON_KEY =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0'
// For Node.js < 22, we need to provide a WebSocket implementation
// Node.js 22+ has native WebSocket support
let wsTransport: any = undefined
if (typeof WebSocket === 'undefined' && typeof process !== 'undefined' && process.versions?.node) {
try {
wsTransport = require('./ws')
} catch (error) {
console.warn('WebSocket not available, Realtime features may not work')
}
}
const supabase = createClient(SUPABASE_URL, ANON_KEY, {
realtime: {
heartbeatIntervalMs: 500,
...(wsTransport && { transport: wsTransport }),
},
})
describe('Supabase Integration Tests', () => {
test('should connect to Supabase instance', async () => {
expect(supabase).toBeDefined()
expect(supabase).toBeInstanceOf(SupabaseClient)
})
describe('PostgREST', () => {
test('should connect to PostgREST API', async () => {
const { data, error } = await supabase.from('todos').select('*').limit(5)
// The default schema includes a 'todos' table, but it might be empty
expect(error).toBeNull()
expect(Array.isArray(data)).toBe(true)
})
// Test creating and deleting data
test('should create and delete a todo', async () => {
// Create a new todo
const { data: createdTodo, error: createError } = await supabase
.from('todos')
.insert({ task: 'Integration Test Todo', is_complete: false })
.select()
.single()
expect(createError).toBeNull()
expect(createdTodo).toBeDefined()
expect(createdTodo!.task).toBe('Integration Test Todo')
expect(createdTodo!.is_complete).toBe(false)
// Delete the created todo
const { error: deleteError } = await supabase.from('todos').delete().eq('id', createdTodo!.id)
expect(deleteError).toBeNull()
// Verify the todo was deleted
const { data: fetchedTodo, error: fetchError } = await supabase
.from('todos')
.select('*')
.eq('id', createdTodo!.id)
.single()
expect(fetchError).not.toBeNull()
expect(fetchedTodo).toBeNull()
})
})
describe('PostgreSQL RLS', () => {
let user1Email: string
let user2Email: string
let user1Id: string
let user2Id: string
let user1TodoId: string
let user2TodoId: string
beforeAll(async () => {
// Create two test users
user1Email = `user1-${Date.now()}@example.com`
user2Email = `user2-${Date.now()}@example.com`
const password = 'password123'
const { data: user1Data } = await supabase.auth.signUp({
email: user1Email,
password,
})
user1Id = user1Data.user!.id
const { data: user2Data } = await supabase.auth.signUp({
email: user2Email,
password,
})
user2Id = user2Data.user!.id
// Create todos for both users
await supabase.auth.signInWithPassword({ email: user1Email, password })
const { data: user1Todo } = await supabase
.from('todos')
.insert({ task: 'User 1 Todo', is_complete: false, user_id: user1Id })
.select()
.single()
user1TodoId = user1Todo!.id
await supabase.auth.signInWithPassword({ email: user2Email, password })
const { data: user2Todo } = await supabase
.from('todos')
.insert({ task: 'User 2 Todo', is_complete: false, user_id: user2Id })
.select()
.single()
user2TodoId = user2Todo!.id
})
afterAll(async () => {
await supabase.auth.signOut()
})
test('should allow anonymous access via RLS policies', async () => {
await supabase.auth.signOut()
const { data, error } = await supabase.from('todos').select('*').limit(5)
expect(error).toBeNull()
expect(Array.isArray(data)).toBe(true)
})
test('should allow authenticated user to access their own data', async () => {
await supabase.auth.signInWithPassword({ email: user1Email, password: 'password123' })
const { data, error } = await supabase
.from('todos')
.select('*')
.eq('id', user1TodoId)
.single()
expect(error).toBeNull()
expect(data).toBeDefined()
expect(data!.task).toBe('User 1 Todo')
})
test('should prevent access to other users data', async () => {
await supabase.auth.signInWithPassword({ email: user1Email, password: 'password123' })
const { data, error } = await supabase
.from('todos')
.select('*')
.eq('id', user2TodoId)
.single()
expect(error).not.toBeNull()
expect(data).toBeNull()
})
test('should allow authenticated user to create their own data', async () => {
await supabase.auth.signInWithPassword({ email: user1Email, password: 'password123' })
const { data, error } = await supabase
.from('todos')
.insert({ task: 'New User 1 Todo', is_complete: false, user_id: user1Id })
.select()
.single()
expect(error).toBeNull()
expect(data).toBeDefined()
expect(data!.task).toBe('New User 1 Todo')
})
test('should allow authenticated user to update their own data', async () => {
await supabase.auth.signInWithPassword({ email: user1Email, password: 'password123' })
const { data, error } = await supabase
.from('todos')
.update({ task: 'Updated User 1 Todo' })
.eq('id', user1TodoId)
.select()
.single()
expect(error).toBeNull()
expect(data).toBeDefined()
expect(data!.task).toBe('Updated User 1 Todo')
})
})
describe('Authentication', () => {
afterAll(async () => {
// Clean up by signing out the user
await supabase.auth.signOut()
})
test('should sign up a user', async () => {
const email = `test-${Date.now()}@example.com`
const password = 'password123'
const { data, error } = await supabase.auth.signUp({
email,
password,
})
expect(error).toBeNull()
expect(data.user).toBeDefined()
expect(data.user!.email).toBe(email)
})
test('should sign in and out successfully', async () => {
const email = `test-${Date.now()}@example.com`
const password = 'password123'
await supabase.auth.signUp({ email, password })
const { data, error } = await supabase.auth.signInWithPassword({ email, password })
expect(error).toBeNull()
expect(data.user).toBeDefined()
expect(data.user!.email).toBe(email)
const { error: signOutError } = await supabase.auth.signOut()
expect(signOutError).toBeNull()
})
test('should get current user', async () => {
const email = `test-${Date.now()}@example.com`
const password = 'password123'
await supabase.auth.signUp({ email, password })
await supabase.auth.signInWithPassword({ email, password })
const { data, error } = await supabase.auth.getUser()
expect(error).toBeNull()
expect(data.user).toBeDefined()
expect(data.user!.email).toBe(email)
})
test('should handle invalid credentials', async () => {
const email = `test-${Date.now()}@example.com`
const password = 'password123'
await supabase.auth.signUp({ email, password })
const { data, error } = await supabase.auth.signInWithPassword({
email,
password: 'wrongpassword',
})
expect(error).not.toBeNull()
expect(data.user).toBeNull()
})
test('should handle non-existent user', async () => {
const email = `nonexistent-${Date.now()}@example.com`
const password = 'password123'
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
})
expect(error).not.toBeNull()
expect(data.user).toBeNull()
})
})
describe('Realtime', () => {
const channelName = `channel-${crypto.randomUUID()}`
let channel: RealtimeChannel
let email: string
let password: string
beforeEach(async () => {
await supabase.auth.signOut()
email = `test-${Date.now()}@example.com`
password = 'password123'
await supabase.auth.signUp({ email, password })
const config = { broadcast: { self: true }, private: true }
channel = supabase.channel(channelName, { config })
})
afterEach(async () => {
await supabase.removeAllChannels()
})
test('is able to connect and broadcast', async () => {
const testMessage = { message: 'test' }
let receivedMessage: any
let subscribed = false
let attempts = 0
channel
.on('broadcast', { event: '*' }, (payload) => (receivedMessage = payload))
.subscribe((status) => {
if (status == 'SUBSCRIBED') subscribed = true
})
// Wait for subscription
while (!subscribed) {
if (attempts > 50) throw new Error('Timeout waiting for subscription')
await new Promise((resolve) => setTimeout(resolve, 100))
attempts++
}
attempts = 0
channel.send({ type: 'broadcast', event: 'test-event', payload: testMessage })
// Wait on message
while (!receivedMessage) {
if (attempts > 50) throw new Error('Timeout waiting for message')
await new Promise((resolve) => setTimeout(resolve, 100))
attempts++
}
expect(receivedMessage).toBeDefined()
expect(supabase.realtime.getChannels().length).toBe(1)
}, 10000)
})
})
describe('Storage API', () => {
const bucket = 'test-bucket'
const filePath = 'test-file.txt'
const fileContent = new Blob(['Hello, Supabase Storage!'], { type: 'text/plain' })
// use service_role key for bypass RLS
const SERVICE_ROLE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY || 'use-service-role-key'
const supabaseWithServiceRole = createClient(SUPABASE_URL, SERVICE_ROLE_KEY, {
realtime: {
heartbeatIntervalMs: 500,
...(wsTransport && { transport: wsTransport }),
},
})
test('upload and list file in bucket', async () => {
// upload
const { data: uploadData, error: uploadError } = await supabaseWithServiceRole.storage
.from(bucket)
.upload(filePath, fileContent, { upsert: true })
expect(uploadError).toBeNull()
expect(uploadData).toBeDefined()
// list
const { data: listData, error: listError } = await supabaseWithServiceRole.storage
.from(bucket)
.list()
expect(listError).toBeNull()
expect(Array.isArray(listData)).toBe(true)
if (!listData) throw new Error('listData is null')
const fileNames = listData.map((f: any) => f.name)
expect(fileNames).toContain('test-file.txt')
// delete file
const { error: deleteError } = await supabaseWithServiceRole.storage
.from(bucket)
.remove([filePath])
expect(deleteError).toBeNull()
})
})