118 lines
3.4 KiB
TypeScript
118 lines
3.4 KiB
TypeScript
import { Client, isApiError } from '@botpress/client'
|
|
import axios from 'axios'
|
|
import findProcess from 'find-process'
|
|
import fslib from 'fs'
|
|
import pathlib from 'path'
|
|
import * as uuid from 'uuid'
|
|
import impl from '../../src'
|
|
import defaults from '../defaults'
|
|
import * as retry from '../retry'
|
|
import { Test } from '../typings'
|
|
import * as utils from '../utils'
|
|
|
|
const handleExitCode = ({ exitCode }: { exitCode: number }) => {
|
|
if (exitCode !== 0) {
|
|
throw new Error(`Command exited with code ${exitCode}`)
|
|
}
|
|
}
|
|
|
|
const fetchDevBotById = async (client: Client, id: string) => {
|
|
const { bot } = await client.getBot({ id }).catch((err) => {
|
|
if (isApiError(err) && err.type === 'ResourceNotFound') {
|
|
return { bot: undefined }
|
|
}
|
|
throw err
|
|
})
|
|
return bot
|
|
}
|
|
|
|
const readBotCache = async (
|
|
botDir: string
|
|
): Promise<{
|
|
botId?: string
|
|
tunnelId?: string
|
|
devId?: string
|
|
}> => {
|
|
const botCache = pathlib.join(botDir, '.botpress', 'project.cache.json')
|
|
if (!fslib.existsSync(botCache)) {
|
|
return {}
|
|
}
|
|
const cacheContent: string = await fslib.promises.readFile(botCache, 'utf-8')
|
|
try {
|
|
return JSON.parse(cacheContent)
|
|
} catch {
|
|
return {}
|
|
}
|
|
}
|
|
|
|
const PORT = 8075
|
|
|
|
export const devBot: Test = {
|
|
name: 'cli should allow creating and running a bot locally',
|
|
handler: async ({ tmpDir, tunnelUrl, dependencies, ...creds }) => {
|
|
const botpressHomeDir = pathlib.join(tmpDir, '.botpresshome')
|
|
const baseDir = pathlib.join(tmpDir, 'bots')
|
|
const botName = uuid.v4()
|
|
const tunnelId = uuid.v4()
|
|
const botDir = pathlib.join(baseDir, botName)
|
|
|
|
const argv = {
|
|
...defaults,
|
|
botpressHome: botpressHomeDir,
|
|
confirm: true,
|
|
...creds,
|
|
}
|
|
|
|
const client = new Client({
|
|
apiUrl: creds.apiUrl,
|
|
token: creds.token,
|
|
workspaceId: creds.workspaceId,
|
|
retry: retry.config,
|
|
})
|
|
|
|
await impl.init({ ...argv, workDir: baseDir, name: botName, type: 'bot', template: 'empty' }).then(handleExitCode)
|
|
await utils.fixBotpressDependencies({ workDir: botDir, target: dependencies })
|
|
await utils.npmInstall({ workDir: botDir }).then(handleExitCode)
|
|
await impl.login({ ...argv }).then(handleExitCode)
|
|
|
|
const cmdPromise = impl
|
|
.dev({ ...argv, workDir: botDir, port: PORT, tunnelUrl, tunnelId, noSecretCaching: true })
|
|
.then(handleExitCode)
|
|
await utils.sleep(5000)
|
|
|
|
const allProcess = await findProcess('port', PORT)
|
|
const [botProcess] = allProcess
|
|
if (allProcess.length > 1) {
|
|
throw new Error(`Expected to find only one process listening on port ${PORT}`)
|
|
}
|
|
if (!botProcess) {
|
|
throw new Error(`Expected to find a process listening on port ${PORT}`)
|
|
}
|
|
|
|
const botCache = await readBotCache(botDir)
|
|
const { devId: botId } = botCache
|
|
if (!botId) {
|
|
throw new Error('Expected devId to be set in project cache')
|
|
}
|
|
|
|
const resp = await axios.get(`http://localhost:${PORT}/health`)
|
|
if (resp.status !== 200) {
|
|
throw new Error(`Expected health endpoint to return 200, got ${resp.status}`)
|
|
}
|
|
|
|
process.kill(botProcess.pid)
|
|
await cmdPromise
|
|
|
|
const apiBot = await fetchDevBotById(client, botId)
|
|
if (!apiBot) {
|
|
throw new Error(`Dev Bot ${botId} should have been created in the backend`)
|
|
}
|
|
|
|
await impl.bots.delete({ ...argv, botRef: apiBot.id }).then(utils.handleExitCode)
|
|
|
|
if (await fetchDevBotById(client, botId)) {
|
|
throw new Error(`Dev bot ${botId} should have been deleted`)
|
|
}
|
|
},
|
|
}
|