Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 45 additions & 19 deletions packages/database/prod/src/main.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'

import { getDatabase, MissingDatabaseConnectionError } from './main.js'
import { getConnectionString, getDatabase, MissingDatabaseConnectionError } from './main.js'

const { mockWaddlerNodePostgres, mockWaddlerNeonHttp, mockPgPool, mockNeonPool, mockNeon, mockNeonConfig } = vi.hoisted(
() => ({
Expand Down Expand Up @@ -35,6 +35,9 @@ vi.mock('ws', () => ({
default: function MockWebSocket() {},
}))

const CONNECTION_STRING = 'postgres://user:pass@localhost:5432/mydb'
const OTHER_CONNECTION_STRING = 'postgres://other:pass@localhost:5432/otherdb'

describe('getDatabase', () => {
const originalEnv = process.env

Expand All @@ -57,55 +60,55 @@ describe('getDatabase', () => {
})

it('returns an object with sql, pool, and connectionString using node-postgres by default', () => {
process.env.NETLIFY_DB_URL = 'postgres://user:pass@localhost:5432/mydb'
process.env.NETLIFY_DB_URL = CONNECTION_STRING

const result = getDatabase()

expect(mockPgPool).toHaveBeenCalledWith({ connectionString: 'postgres://user:pass@localhost:5432/mydb' })
expect(mockPgPool).toHaveBeenCalledWith({ connectionString: CONNECTION_STRING })
expect(mockWaddlerNodePostgres).toHaveBeenCalledWith({ client: expect.any(Object) })
expect(mockWaddlerNeonHttp).not.toHaveBeenCalled()
expect(mockNeonPool).not.toHaveBeenCalled()
expect(result).toEqual({
driver: 'server',
sql: 'node-postgres-sql',
pool: expect.any(Object),
connectionString: 'postgres://user:pass@localhost:5432/mydb',
connectionString: CONNECTION_STRING,
})
})

it('uses node-postgres when NETLIFY_DB_DRIVER is "server"', () => {
process.env.NETLIFY_DB_URL = 'postgres://user:pass@localhost:5432/mydb'
process.env.NETLIFY_DB_URL = CONNECTION_STRING
process.env.NETLIFY_DB_DRIVER = 'server'

const result = getDatabase()

expect(mockWaddlerNodePostgres).toHaveBeenCalled()
expect(mockWaddlerNeonHttp).not.toHaveBeenCalled()
expect(result.connectionString).toBe('postgres://user:pass@localhost:5432/mydb')
expect(result.connectionString).toBe(CONNECTION_STRING)
})

it('uses neon-http for sql and neon Pool for pool when NETLIFY_DB_DRIVER is "serverless"', () => {
process.env.NETLIFY_DB_URL = 'postgres://user:pass@localhost:5432/mydb'
process.env.NETLIFY_DB_URL = CONNECTION_STRING
process.env.NETLIFY_DB_DRIVER = 'serverless'

const result = getDatabase()

expect(mockWaddlerNeonHttp).toHaveBeenCalledWith({ client: 'neon-http-client' })
expect(mockNeonPool).toHaveBeenCalledWith({ connectionString: 'postgres://user:pass@localhost:5432/mydb' })
expect(mockNeonPool).toHaveBeenCalledWith({ connectionString: CONNECTION_STRING })
expect(mockWaddlerNodePostgres).not.toHaveBeenCalled()
expect(mockPgPool).not.toHaveBeenCalled()
expect(mockNeon).toHaveBeenCalledWith('postgres://user:pass@localhost:5432/mydb')
expect(mockNeon).toHaveBeenCalledWith(CONNECTION_STRING)
expect(result).toEqual({
driver: 'serverless',
sql: 'neon-http-sql',
pool: expect.any(Object),
httpClient: 'neon-http-client',
connectionString: 'postgres://user:pass@localhost:5432/mydb',
connectionString: CONNECTION_STRING,
})
})

it('sets neonConfig.webSocketConstructor from ws when WebSocket is not globally available', () => {
process.env.NETLIFY_DB_URL = 'postgres://user:pass@localhost:5432/mydb'
process.env.NETLIFY_DB_URL = CONNECTION_STRING
process.env.NETLIFY_DB_DRIVER = 'serverless'
delete mockNeonConfig.webSocketConstructor

Expand All @@ -122,7 +125,7 @@ describe('getDatabase', () => {
})

it('does not override neonConfig.webSocketConstructor if already set', () => {
process.env.NETLIFY_DB_URL = 'postgres://user:pass@localhost:5432/mydb'
process.env.NETLIFY_DB_URL = CONNECTION_STRING
process.env.NETLIFY_DB_DRIVER = 'serverless'
const existingWs = function ExistingWebSocket() {}
mockNeonConfig.webSocketConstructor = existingWs
Expand All @@ -133,20 +136,43 @@ describe('getDatabase', () => {
})

it('allows override via connectionString option', () => {
process.env.NETLIFY_DB_URL = 'postgres://user:pass@localhost:5432/mydb'
process.env.NETLIFY_DB_URL = CONNECTION_STRING

const result = getDatabase({ connectionString: 'postgres://other:pass@localhost:5432/otherdb' })
const result = getDatabase({ connectionString: OTHER_CONNECTION_STRING })

expect(mockPgPool).toHaveBeenCalledWith({ connectionString: 'postgres://other:pass@localhost:5432/otherdb' })
expect(result.connectionString).toBe('postgres://other:pass@localhost:5432/otherdb')
expect(mockPgPool).toHaveBeenCalledWith({ connectionString: OTHER_CONNECTION_STRING })
expect(result.connectionString).toBe(OTHER_CONNECTION_STRING)
})

it('uses NETLIFY_DB_URL environment variable', () => {
process.env.NETLIFY_DB_URL = 'postgres://user:pass@localhost:5432/mydb'
process.env.NETLIFY_DB_URL = CONNECTION_STRING

const result = getDatabase()

expect(mockPgPool).toHaveBeenCalledWith({ connectionString: 'postgres://user:pass@localhost:5432/mydb' })
expect(result.connectionString).toBe('postgres://user:pass@localhost:5432/mydb')
expect(mockPgPool).toHaveBeenCalledWith({ connectionString: CONNECTION_STRING })
expect(result.connectionString).toBe(CONNECTION_STRING)
})
})

describe('getConnectionString', () => {
const originalEnv = process.env

beforeEach(() => {
process.env = { ...originalEnv }
delete process.env.NETLIFY_DB_URL
})

afterEach(() => {
process.env = originalEnv
})

it('returns the connection string from NETLIFY_DB_URL', () => {
process.env.NETLIFY_DB_URL = CONNECTION_STRING

expect(getConnectionString()).toBe(CONNECTION_STRING)
})

it('throws MissingDatabaseConnectionError when NETLIFY_DB_URL is not set', () => {
expect(() => getConnectionString()).toThrow(MissingDatabaseConnectionError)
})
})
11 changes: 11 additions & 0 deletions packages/database/prod/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ export interface ServerlessDatabaseConnection {

export type DatabaseConnection = ServerDatabaseConnection | ServerlessDatabaseConnection

export function getConnectionString(): string {
const env = getEnvironment()
const connectionString = env.get('NETLIFY_DB_URL')

if (!connectionString) {
throw new MissingDatabaseConnectionError()
}

return connectionString
}

export function getDatabase(options: GetDatabaseOptions = {}): DatabaseConnection {
const env = getEnvironment()
const connectionString = options.connectionString ?? env.get('NETLIFY_DB_URL')
Expand Down
Loading