Skip to content

Commit

Permalink
api: Add initial test
Browse files Browse the repository at this point in the history
  • Loading branch information
xemle committed Sep 27, 2023
1 parent 99bfae0 commit d5d899b
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 1 deletion.
5 changes: 5 additions & 0 deletions packages/api-server/.taprc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
disable-coverage: true
allow-empty-coverage: true
allow-incomplete-coverage: true
include:
- '*.test.js'
111 changes: 111 additions & 0 deletions packages/api-server/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
const { spawn } = require('child_process')
const t = require('tap')
const fetch = require('node-fetch')
const fs = require('fs/promises')

const { runCli, waitForUrl } = require('./test-utils')

t.test('API Server', async t => {
let child
const port = 23080
const url = `http://localhost:${port}`

t.before(async () => {
child = runCli('node', ['index.js'], { env: {PORT: '23080'}})
console.log(`Starting api server with pid ${child.pid}`)
await waitForUrl(`${url}/health`)
console.log(`Api server is up and running on ${url}`)
})

t.after(async () => {
return new Promise((resolve, rejects) => {
console.log(`Stop server`)
process.kill(child.pid)
child.on('exit', code => code == 0 ? resolve() : rejects(new Error(`Server existed with code ${code}`)))
})
})

t.test('embeddings', async t => {
const body = await fs.readFile('sample.jpg')
const data = await fetch(`${url}/embeddings`, {
method: 'POST',
body,
headers: {
'Content-Type': 'image/jpeg'
}
}).then(res => res.json())
t.match(data, {
srcSha1sum: '17f5e000be30d7915fe161a03db2773de279df1f',
model: 'mobilenet',
version: 'v1_1.0',
})
t.match(data.data.length, 1024)
})

t.test('objects', async t => {
const body = await fs.readFile('sample-2.jpg')
const data = await fetch(`${url}/objects`, {
method: 'POST',
body,
headers: {
'Content-Type': 'image/jpeg'
}
}).then(res => res.json())
t.match(data, {
srcSha1sum: 'c3328f60fd4fc6c85bfff3fdba9e2f067c135e5f',
model: 'cocossd',
version: 'mobilenet_v2',
width: 300,
height: 188,
})
t.match(data.data.length, 20)
t.same(data.data.map(o => o.class), [
'person',
'person',
'person',
'person',
'book',
'book',
'book',
'book',
'chair',
'book',
'dining table',
'potted plant',
'book',
'person',
'book',
'book',
'book',
'potted plant',
'book',
'book',
])
})

t.test('faces', async t => {
const body = await fs.readFile('sample-2.jpg')
const data = await fetch(`${url}/faces`, {
method: 'POST',
body,
headers: {
'Content-Type': 'image/jpeg'
}
}).then(res => res.json())
t.match(data, {
srcSha1sum: 'c3328f60fd4fc6c85bfff3fdba9e2f067c135e5f',
model: 'face-api',
width: 300,
height: 188,
})
t.match(data.data.length, 6)
t.same(data.data.map(o => o.gender), [
'male',
'male',
'male',
'male',
'male',
'female'
])
})
})
4 changes: 3 additions & 1 deletion packages/api-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"description": "Public api server for HomeGallery",
"main": "index.js",
"scripts": {
"serve": "node index.js"
"serve": "node index.js",
"test": "tap",
"watch:test": "tap repl w"
},
"keywords": [
"HomeGallery",
Expand Down
4 changes: 4 additions & 0 deletions packages/api-server/src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ const asyncMiddleware = require('./server/async-middleware');
const readJpeg = require('./utils/read-jpeg');
const toSha1 = require('./utils/sha1');

const t0 = Date.now()
const routes = (app, maxBytes, modelConfig, embeddings, objects, faces) => {
app.get('/health', (_, res) => {
res.send({health: 'OK', uptime: Date.now() - t0})
})
app.post('/embeddings', [
isMimeMiddleware('image/jpeg'),
binaryBodyMiddleware(maxBytes),
Expand Down
63 changes: 63 additions & 0 deletions packages/api-server/test-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const { spawn } = require('child_process')
const fetch = require('node-fetch')

const runCli = (command, args = [], options = {}, onExit = () => true) => {
const env = {
...process.env,
...options.env
}
const child = spawn(command, args, { shell: false, stdio: 'pipe', ...options, env })
const stdout = []
const stderr = []

child.stdout.on('data', data => {
console.log(data.toString('utf8'))
stdout.push(data)
})
child.stderr.on('data', data => stderr.push(data))

child.on('close', (code, signal) => {
onExit({
code,
signal,
stdout: Buffer.concat(stdout).toString('utf8'),
stderr: Buffer.concat(stdout).toString('utf8')
})
})

return child
}

const delayAsync = async (delay = 0) => new Promise(resolve => setTimeout(resolve, delay))

const waitFor = async (fn, retryDelay = 0) => {
return fn().catch(() => delayAsync(retryDelay).then(() => waitFor(fn, retryDelay)))
}

const waitForUrl = (url, retryDelay = 125) => {
return waitFor(() => {
const controller = new AbortController()
const timer = setTimeout(() => {
controller.abort()
}, 1000)
return fetch(url, {
signal: controller.signal
}).then(res => {
clearTimeout(timer)
if (!res.ok) {
throw new Error(`Failed to fetch ${url}: ${res.status}`)
}
return res
})
.catch(e => {
clearTimeout(timer)
throw e
})
}, retryDelay)
}

module.exports = {
runCli,
waitFor,
waitForUrl
}

0 comments on commit d5d899b

Please sign in to comment.