mirror of
https://github.com/kevinthedang/discord-ollama.git
synced 2025-12-12 11:56:06 -05:00
Initialize unit testing and code coverage
* add: vitest configs * added vitest scripts to package * test coverage of src code * initial unit testing * added new testing workflows * comments added, overlapping tests removed * decouple env, tests --------- Co-authored-by: Kevin Dang <kevinthedang_1@outlook.com>
This commit is contained in:
82
.github/workflows/build-test.yml
vendored
82
.github/workflows/build-test.yml
vendored
@@ -1,82 +0,0 @@
|
||||
name: Builds
|
||||
run-name: Validate Node and Docker Builds
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
Discord-Node-Build: # test if the node install and run
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node Environment v18.18.2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.18.2
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install Project Dependencies
|
||||
run: |
|
||||
npm install
|
||||
|
||||
- name: Build Application
|
||||
run: |
|
||||
npm run build
|
||||
|
||||
- name: Create Environment Variables
|
||||
run: |
|
||||
touch .env
|
||||
echo CLIENT_TOKEN = ${{ secrets.BOT_TOKEN }} >> .env
|
||||
echo GUILD_ID = ${{ secrets.GUILD_ID }} >> .env
|
||||
echo CHANNEL_ID = ${{ secrets.CHANNEL_ID }} >> .env
|
||||
echo MODEL = ${{ secrets.MODEL }} >> .env
|
||||
echo CLIENT_UID = ${{ secrets.CLIENT_UID }} >> .env
|
||||
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
|
||||
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
|
||||
|
||||
# set -e ensures if nohup fails, this section fails
|
||||
- name: Startup Discord Bot Client
|
||||
run: |
|
||||
set -e
|
||||
nohup npm run prod &
|
||||
|
||||
Discord-Ollama-Container-Build: # test docker build and run
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node Environment v18.18.2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.18.2
|
||||
cache: 'npm'
|
||||
|
||||
- name: Create Environment Variables
|
||||
run: |
|
||||
touch .env
|
||||
echo CLIENT_TOKEN = ${{ secrets.BOT_TOKEN }} >> .env
|
||||
echo GUILD_ID = ${{ secrets.GUILD_ID }} >> .env
|
||||
echo CHANNEL_ID = ${{ secrets.CHANNEL_ID }} >> .env
|
||||
echo MODEL = ${{ secrets.MODEL }} >> .env
|
||||
echo CLIENT_UID = ${{ secrets.CLIENT_UID }} >> .env
|
||||
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
|
||||
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
|
||||
|
||||
- name: Setup Docker Network and Images
|
||||
run: |
|
||||
npm run docker:start-cpu
|
||||
|
||||
- name: Check Images Exist
|
||||
run: |
|
||||
(docker images | grep -q 'discord/bot' && docker images | grep -qE 'ollama/ollama') || exit 1
|
||||
|
||||
- name: Check Containers Exist
|
||||
run: |
|
||||
(docker ps | grep -q 'ollama' && docker ps | grep -q 'discord') || exit 1
|
||||
|
||||
81
.github/workflows/build.yml
vendored
Normal file
81
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
name: Builds
|
||||
run-name: Validate Node and Docker Builds
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
Discord-Node-Build: # test if the node install and run
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node Environment v18.18.2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.18.2
|
||||
cache: "npm"
|
||||
|
||||
- name: Install Project Dependencies
|
||||
run: |
|
||||
npm install
|
||||
|
||||
- name: Build Application
|
||||
run: |
|
||||
npm run build
|
||||
|
||||
- name: Create Environment Variables
|
||||
run: |
|
||||
touch .env
|
||||
echo CLIENT_TOKEN = ${{ secrets.BOT_TOKEN }} >> .env
|
||||
echo GUILD_ID = ${{ secrets.GUILD_ID }} >> .env
|
||||
echo CHANNEL_ID = ${{ secrets.CHANNEL_ID }} >> .env
|
||||
echo MODEL = ${{ secrets.MODEL }} >> .env
|
||||
echo CLIENT_UID = ${{ secrets.CLIENT_UID }} >> .env
|
||||
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
|
||||
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
|
||||
|
||||
# set -e ensures if nohup fails, this section fails
|
||||
- name: Startup Discord Bot Client
|
||||
run: |
|
||||
set -e
|
||||
nohup npm run prod &
|
||||
|
||||
Discord-Ollama-Container-Build: # test docker build and run
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node Environment v18.18.2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.18.2
|
||||
cache: "npm"
|
||||
|
||||
- name: Create Environment Variables
|
||||
run: |
|
||||
touch .env
|
||||
echo CLIENT_TOKEN = ${{ secrets.BOT_TOKEN }} >> .env
|
||||
echo GUILD_ID = ${{ secrets.GUILD_ID }} >> .env
|
||||
echo CHANNEL_ID = ${{ secrets.CHANNEL_ID }} >> .env
|
||||
echo MODEL = ${{ secrets.MODEL }} >> .env
|
||||
echo CLIENT_UID = ${{ secrets.CLIENT_UID }} >> .env
|
||||
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
|
||||
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
|
||||
|
||||
- name: Setup Docker Network and Images
|
||||
run: |
|
||||
npm run docker:start-cpu
|
||||
|
||||
- name: Check Images Exist
|
||||
run: |
|
||||
(docker images | grep -q 'discord/bot' && docker images | grep -qE 'ollama/ollama') || exit 1
|
||||
|
||||
- name: Check Containers Exist
|
||||
run: |
|
||||
(docker ps | grep -q 'ollama' && docker ps | grep -q 'discord') || exit 1
|
||||
71
.github/workflows/test.yml
vendored
Normal file
71
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
name: Tests
|
||||
run-name: Test source code for errors
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- unit-testing
|
||||
|
||||
jobs:
|
||||
Discord-Node-Test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node Environment v18.18.2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.18.2
|
||||
cache: "npm"
|
||||
|
||||
- name: Install Project Dependencies
|
||||
run: |
|
||||
npm install
|
||||
|
||||
- name: Create Environment Variables
|
||||
run: |
|
||||
touch .env
|
||||
echo CLIENT_TOKEN = ${{ secrets.BOT_TOKEN }} >> .env
|
||||
echo GUILD_ID = ${{ secrets.GUILD_ID }} >> .env
|
||||
echo CHANNEL_ID = ${{ secrets.CHANNEL_ID }} >> .env
|
||||
echo MODEL = ${{ secrets.MODEL }} >> .env
|
||||
echo CLIENT_UID = ${{ secrets.CLIENT_UID }} >> .env
|
||||
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
|
||||
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
|
||||
|
||||
- name: Test Application
|
||||
run: |
|
||||
npm run test:run
|
||||
|
||||
Discord-Ollama-Container-Test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node Environment v18.18.2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.18.2
|
||||
cache: "npm"
|
||||
|
||||
- name: Create Environment Variables
|
||||
run: |
|
||||
touch .env
|
||||
echo CLIENT_TOKEN = ${{ secrets.BOT_TOKEN }} >> .env
|
||||
echo GUILD_ID = ${{ secrets.GUILD_ID }} >> .env
|
||||
echo CHANNEL_ID = ${{ secrets.CHANNEL_ID }} >> .env
|
||||
echo MODEL = ${{ secrets.MODEL }} >> .env
|
||||
echo CLIENT_UID = ${{ secrets.CLIENT_UID }} >> .env
|
||||
echo OLLAMA_IP = ${{ secrets.OLLAMA_IP }} >> .env
|
||||
echo OLLAMA_PORT = ${{ secrets.OLLAMA_PORT }} >> .env
|
||||
|
||||
- name: Setup Docker Network and Images
|
||||
run: |
|
||||
npm run docker:start-cpu
|
||||
|
||||
- name: Test Docker Container
|
||||
run: |
|
||||
npm run docker:test
|
||||
@@ -4,7 +4,8 @@
|
||||
<h3><a href="#"></a>Ollama as your Discord AI Assistant</h3>
|
||||
<p><a href="#"></a><a href="https://creativecommons.org/licenses/by/4.0/"><img alt="License" src="https://img.shields.io/badge/License-CC_BY_4.0-darkgreen.svg" /></a>
|
||||
<a href="#"></a><a href="https://github.com/kevinthedang/discord-ollama/releases/latest"><img alt="Release" src="https://img.shields.io/github/v/release/kevinthedang/discord-ollama?logo=github" /></a>
|
||||
<a href="#"></a><a href="https://github.com/kevinthedang/discord-ollama/actions/workflows/build-test.yml"><img alt="Build Status" src="https://github.com/kevinthedang/discord-ollama/actions/workflows/build-test.yml/badge.svg" /></a>
|
||||
<a href="#"></a><a href="https://github.com/kevinthedang/discord-ollama/actions/workflows/build.yml"><img alt="Build Status" src="https://github.com/kevinthedang/discord-ollama/actions/workflows/build.yml/badge.svg" /></a>
|
||||
<a href="#"></a><a href="https://github.com/kevinthedang/discord-ollama/actions/workflows/test.yml"><img alt="Testing Status" src="https://github.com/kevinthedang/discord-ollama/actions/workflows/test.yml/badge.svg" /></a>
|
||||
</div>
|
||||
|
||||
## About/Goals
|
||||
|
||||
1846
package-lock.json
generated
1846
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,8 @@
|
||||
"dev-tsx": "tsx watch src/index.ts",
|
||||
"dev-mon": "nodemon --config nodemon.json src/index.ts",
|
||||
"build": "tsc",
|
||||
"test:run": "vitest run",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"prod": "node .",
|
||||
"client": "npm run build && npm run prod",
|
||||
"clean": "docker compose down && docker rmi $(docker images | grep $(node -p \"require('./package.json').version\") | tr -s ' ' | cut -d ' ' -f 3) && docker rmi $(docker images --filter \"dangling=true\" -q --no-trunc)",
|
||||
@@ -17,6 +19,7 @@
|
||||
"docker:clean": "docker rmi $(docker images --filter \"dangling=true\" -q --no-trunc)",
|
||||
"docker:network": "docker network create --subnet=172.18.0.0/16 ollama-net",
|
||||
"docker:build": "docker build --no-cache -t discord/bot:$(node -p \"require('./package.json').version\") .",
|
||||
"docker:test": "docker run -d --rm -v discord:/src/app --name test discord/bot:$(node -p \"require('./package.json').version\") npm run test:run",
|
||||
"docker:client": "docker run -d -v discord:/src/app --name discord --network ollama-net --ip 172.18.0.3 discord/bot:$(node -p \"require('./package.json').version\")",
|
||||
"docker:ollama": "docker run -d --gpus=all -v ollama:/root/.ollama -p 11434:11434 --name ollama --network ollama-net --ip 172.18.0.2 ollama/ollama:latest",
|
||||
"docker:ollama-cpu": "docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama --network ollama-net --ip 172.18.0.2 ollama/ollama:latest"
|
||||
@@ -30,10 +33,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.5",
|
||||
"@vitest/coverage-v8": "^1.6.0",
|
||||
"nodemon": "^3.0.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsx": "^4.6.2",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "^5.3.3",
|
||||
"vitest": "^1.6.0"
|
||||
},
|
||||
"type": "module",
|
||||
"engines": {
|
||||
|
||||
27
tests/commands.test.ts
Normal file
27
tests/commands.test.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
// describe marks a test suite
|
||||
// expect takes a value from an expression
|
||||
// it marks a test case
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import commands from '../src/commands'
|
||||
|
||||
/**
|
||||
* Commands test suite, tests the commands object
|
||||
* Each command is to be tested elsewhere, this file
|
||||
* is to ensure that the commands object is defined.
|
||||
*
|
||||
* @param name name of the test suite
|
||||
* @param fn function holding tests to run
|
||||
*/
|
||||
describe('#commands', () => {
|
||||
// test definition of commands object
|
||||
it('references defined object', () => {
|
||||
// toBe compares the value to the expected value
|
||||
expect(typeof commands).toBe('object')
|
||||
})
|
||||
|
||||
// test specific commands in the object
|
||||
it('references specific commands', () => {
|
||||
const commandsString = commands.map(e => e.name).join(', ')
|
||||
expect(commandsString).toBe('thread, message-style, message-stream, toggle-chat, shutoff, modify-capacity')
|
||||
})
|
||||
})
|
||||
23
tests/events.test.ts
Normal file
23
tests/events.test.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import events from '../src/events'
|
||||
|
||||
/**
|
||||
* Events test suite, tests the events object
|
||||
* Each event is to be tested elsewhere, this file
|
||||
* is to ensure that the events object is defined.
|
||||
*
|
||||
* @param name name of the test suite
|
||||
* @param fn function holding tests to run
|
||||
*/
|
||||
describe('#events', () => {
|
||||
// test definition of events object
|
||||
it('references defined object', () => {
|
||||
expect(typeof events).toBe('object')
|
||||
})
|
||||
|
||||
// test specific events in the object
|
||||
it('references specific events', () => {
|
||||
const eventsString = events.map(e => e.key.toString()).join(', ')
|
||||
expect(eventsString).toBe('ready, messageCreate, interactionCreate')
|
||||
})
|
||||
})
|
||||
50
tests/getEnvVar.test.ts
Normal file
50
tests/getEnvVar.test.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getEnvVar } from '../src/utils'
|
||||
|
||||
/**
|
||||
* getEnvVar test suite, tests the getEnvVar function
|
||||
*
|
||||
* @param name name of the test suite
|
||||
* @param fn function holding tests to run
|
||||
*/
|
||||
describe('#getEnvVar', () => {
|
||||
// dummy set of keys
|
||||
const keys = {
|
||||
clientToken: 'CLIENT_TOKEN',
|
||||
}
|
||||
|
||||
// set keys in environment
|
||||
process.env['clientToken'] = keys.clientToken
|
||||
|
||||
// test for non-empty string
|
||||
it('returns a non-empty string', () => {
|
||||
expect(getEnvVar('CLIENT_TOKEN')).not.toBe('')
|
||||
})
|
||||
|
||||
// test for string type
|
||||
it('returns a string', () => {
|
||||
expect(typeof getEnvVar('CLIENT_TOKEN')).toBe('string')
|
||||
})
|
||||
|
||||
// test for distinct key
|
||||
it('returns a distinct key', () => {
|
||||
expect(getEnvVar('CLIENT_TOKEN')).toEqual(process.env[keys.clientToken])
|
||||
})
|
||||
|
||||
// test for fallback case
|
||||
it('returns a fallback', () => {
|
||||
expect(getEnvVar('NON_EXISTENT_KEY', 'fallback')).toBe('fallback')
|
||||
})
|
||||
|
||||
// test that all keys are consistently found
|
||||
it('returns all keys found', () => {
|
||||
for (const key in keys) {
|
||||
expect(getEnvVar(key)).toEqual(keys[key])
|
||||
}
|
||||
})
|
||||
|
||||
// test that an error is thrown if key is not found
|
||||
it('throws an error if key is not found', () => {
|
||||
expect(() => getEnvVar('NON_EXISTENT_KEY')).toThrowError()
|
||||
})
|
||||
})
|
||||
17
tests/mentionClean.test.ts
Normal file
17
tests/mentionClean.test.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { clean } from '../src/utils/mentionClean'
|
||||
import { getEnvVar } from '../src/utils'
|
||||
|
||||
/**
|
||||
* MentionClean test suite, tests the clean function
|
||||
*
|
||||
* @param name name of the test suite
|
||||
* @param fn function holding tests to run
|
||||
*/
|
||||
describe('#clean', () => {
|
||||
// test for id removal from message
|
||||
it('removes the mention from a message', () => {
|
||||
const message = `<@${getEnvVar('CLIENT_UID')}> Hello, World!`
|
||||
expect(clean(message)).toBe('Hello, World!')
|
||||
})
|
||||
})
|
||||
62
tests/queue.test.ts
Normal file
62
tests/queue.test.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Queue } from '../src/queues/queue'
|
||||
|
||||
/**
|
||||
* Queue test suite, tests the Queue class
|
||||
*
|
||||
* @param name name of the test suite
|
||||
* @param fn function holding tests to run
|
||||
*/
|
||||
describe('#queue', () => {
|
||||
let queue= new Queue<string>()
|
||||
|
||||
// test for queue creation
|
||||
it('creates a new queue', () => {
|
||||
expect(queue).not.toBeNull()
|
||||
})
|
||||
|
||||
// test for queue capacity creation
|
||||
it('adds specific capacity to the queue', () => {
|
||||
queue = new Queue<string>(2)
|
||||
expect(queue).not.toBeNull()
|
||||
})
|
||||
|
||||
// test for enqueue success, size update
|
||||
it('adds items to the queue', () => {
|
||||
queue.enqueue('hello')
|
||||
expect(queue.size()).toBe(1)
|
||||
})
|
||||
|
||||
// test for multiple enqueue success, size update
|
||||
it('adds multiple items to the queue', () => {
|
||||
queue.enqueue('world')
|
||||
expect(queue.size()).toBe(2)
|
||||
})
|
||||
|
||||
// test for enqueue failure upon capacity overflow
|
||||
it('throws an error when the queue is full', () => {
|
||||
expect(() => queue.enqueue('!')).toThrowError()
|
||||
})
|
||||
|
||||
// test for getItems success
|
||||
it('returns all items in the queue', () => {
|
||||
expect(queue.getItems()).toEqual(['hello', 'world'])
|
||||
})
|
||||
|
||||
// test for pop success, size update
|
||||
it('removes an item from the front of the queue', () => {
|
||||
queue.pop()
|
||||
expect(queue.size() && queue.getItems()[0]).toBe(1 && 'hello')
|
||||
})
|
||||
|
||||
// test for dequeue success, size update
|
||||
it('removes an item from the back of the queue', () => {
|
||||
queue.dequeue()
|
||||
expect(queue.size() && queue.getItems()[0]).toBe(0 && 'world')
|
||||
})
|
||||
|
||||
// test for getItems success with nothing in the list
|
||||
it('returns an empty array when the queue is empty', () => {
|
||||
expect(queue.getItems()).toEqual([])
|
||||
})
|
||||
})
|
||||
12
vitest.config.ts
Normal file
12
vitest.config.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { defineConfig, configDefaults } from 'vitest/config'
|
||||
|
||||
// config for vitest
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true, // <-- reduces test file imports
|
||||
coverage: {
|
||||
exclude: [...configDefaults.exclude, 'build/*'], // <-- exclude JS build
|
||||
reporter: ['text', 'html'] // <-- reports in text, html
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user