Skip to content

Commit

Permalink
run tests in GHA
Browse files Browse the repository at this point in the history
  • Loading branch information
karl-run committed Feb 3, 2024
1 parent 635d20c commit 0897133
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 23 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Run Tests

on: [push]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
- run: bun install --frozen-lockfile
- name: Start server
run: docker-compose up --build -d
- name: Run Tests
run: bun test
13 changes: 10 additions & 3 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@ services:
api:
build: .
ports:
- "6969:6969"
- '6969:6969'
environment:
- DOCKER_COMPOSE=true
depends_on:
- db
db:
image: "postgres:15"
image: 'postgres:15'
ports:
- "5432:5432"
- '5432:5432'
environment:
- POSTGRES_PASSWORD=postgres
- POSTGRES_USER=postgres
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 30s
timeout: 10s
retries: 3
48 changes: 34 additions & 14 deletions src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@ import { cwd } from 'node:process'

const host = Bun.env.DOCKER_COMPOSE ? 'db' : 'localhost'

export const client = new Client({
host: host,
port: 5432,
database: 'postgres',
user: 'postgres',
password: 'postgres',
})
let _client: Client | null = null

export async function initDb() {
console.info('Running migrations if needed')
export function getClient(): Client {
if (_client == null) {
throw new Error('Database not initialized')
}

await connectWithRetry()
return _client
}

export async function initDb() {
console.info('Connecting to database')
const client = await connectWithRetry()

console.info('Running migrations if needed')
const authorsExistQuery = await client.query(`
SELECT EXISTS (SELECT
FROM information_schema.tables
Expand All @@ -43,18 +45,36 @@ export async function initDb() {
}
}

function createClient() {
console.info(`DB host is ${host}`)

return new Client({
host: host,
port: 5432,
database: 'postgres',
user: 'postgres',
password: 'postgres',
})
}

async function connectWithRetry() {
let retry = 0
while (true) {
try {
await client.connect()
break
const freshClient = createClient()
await freshClient.connect()

console.info('Connected to successfully to database')

_client = freshClient

return _client
} catch (e) {
retry++

if (retry > 3) throw e
console.error(`Unable to connect to database, retrying in 3 second (${retry}/3)`)
await new Promise((resolve) => setTimeout(resolve, 3000))
console.error(`Unable to connect to database, retrying in 10 second (${retry}/3)`)
await new Promise((resolve) => setTimeout(resolve, 5000))
}
}
}
27 changes: 26 additions & 1 deletion src/server.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { test, describe, expect, beforeAll, beforeEach } from 'bun:test'
import { test, describe, expect, beforeEach, beforeAll } from 'bun:test'

describe('API sanity checks', () => {
beforeAll(async () => {
await serverReady()
})

beforeEach(async () => {
await fetch('http://localhost:6969/dev/re-seed', { method: 'POST' })
})
Expand Down Expand Up @@ -115,3 +119,24 @@ describe('API sanity checks', () => {
expect(authorBooksData).toHaveLength(1)
})
})

async function serverReady(): Promise<void> {
let waits = 0
while (true) {
if (waits > 30) {
throw new Error('Server did not start in time')
}

try {
const res = await fetch('http://localhost:6969/dev/ready')
if (res.status === 200 && ((await res.json()) as { status: string }).status === 'ok') {
break
}
} catch (e) {
// ignore
} finally {
waits++
await new Promise((resolve) => setTimeout(resolve, 1000))
}
}
}
19 changes: 14 additions & 5 deletions src/server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Elysia, t } from 'elysia'
import { client, initDb } from './db.ts'
import { getClient, initDb } from './db.ts'
import swagger from '@elysiajs/swagger'
import { DbAuthor, DbBook } from './db-types.ts'
import path from 'path'
Expand All @@ -22,7 +22,7 @@ const app = new Elysia()
.get(
'authors',
async () => {
const result = await client.query<DbAuthor>('SELECT * FROM authors')
const result = await getClient().query<DbAuthor>('SELECT * FROM authors')

return result.rows.map((it) => ({
uuid: it.uuid,
Expand All @@ -44,6 +44,7 @@ const app = new Elysia()
.get(
'author/:uuid',
async ({ params: { uuid } }) => {
const client = getClient()
const result = await client.query<DbAuthor>('SELECT * FROM authors WHERE uuid = $1', [uuid])

if (result.rows.length === 0) {
Expand Down Expand Up @@ -76,6 +77,7 @@ const app = new Elysia()
.post(
'author',
async ({ body, set }) => {
const client = getClient()
if (await client.query('SELECT * FROM authors WHERE name = $1', [body.name]).then((it) => it.rows.length > 0)) {
set.status = 'Conflict'
return null
Expand Down Expand Up @@ -106,6 +108,7 @@ const app = new Elysia()
.delete(
'author/:uuid',
async ({ params: { uuid }, set }) => {
const client = getClient()
if (await client.query('SELECT * FROM authors WHERE uuid = $1', [uuid]).then((it) => it.rows.length === 0)) {
set.status = 'Not Found'
return null
Expand All @@ -130,7 +133,7 @@ const app = new Elysia()
.get(
'author/:uuid/books',
async ({ params: { uuid } }) => {
const result = await client.query<Pick<DbBook, 'title' | 'published_date' | 'isbn'>>(
const result = await getClient().query<Pick<DbBook, 'title' | 'published_date' | 'isbn'>>(
`SELECT title, published_date, isbn FROM books
LEFT JOIN authors a on a.id = books.author_id
WHERE a.uuid = $1`,
Expand All @@ -157,7 +160,7 @@ const app = new Elysia()
.get(
'books',
async () => {
const result = await client.query<DbBook>('SELECT * FROM books')
const result = await getClient().query<DbBook>('SELECT * FROM books')

return result.rows.map((it) => ({
title: it.title,
Expand All @@ -179,6 +182,8 @@ const app = new Elysia()
.post(
'book',
async ({ body, set }) => {
const client = getClient()

if (await client.query('SELECT * FROM books WHERE isbn = $1', [body.isbn]).then((it) => it.rows.length > 0)) {
set.status = 'Conflict'
return null
Expand Down Expand Up @@ -210,6 +215,7 @@ const app = new Elysia()
},
)
.post('dev/re-seed', async () => {
const client = getClient()
await client.query('BEGIN')
await client.query('DELETE FROM books')
await client.query('DELETE FROM authors')
Expand All @@ -219,13 +225,16 @@ const app = new Elysia()

return { success: true }
})
.get('dev/ready', async () => {
return { status: 'ok' }
})
.onError((err) => {
console.error(err)
})

app.onStop(async () => {
console.log('Stopping server, closing database connection')
await client.end()
await getClient().end()
})

app.listen(6969)
Expand Down

0 comments on commit 0897133

Please sign in to comment.