Skip to content

Merge pull request #748 from LLOneBot/dev #61

Merge pull request #748 from LLOneBot/dev

Merge pull request #748 from LLOneBot/dev #61

Workflow file for this run

name: Test
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
unit-test:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
- name: Install dependencies
run: |
corepack enable
yarn install --no-immutable
cd src/webui/FE && npm install
- name: Run unit tests
run: npm run test:unit
- name: Run WebUI tests
run: npm run test:webui
e2e-test:
name: E2E Tests
runs-on: [self-hosted, llbot-test]
needs: [unit-test]
if: github.event_name == 'pull_request'
steps:
- name: Cleanup root-owned files
run: sudo rm -rf ${{ github.workspace }}/workspace1 ${{ github.workspace }}/workspace2 ${{ github.workspace }}/test/onebot11-api-test/node_modules
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
- name: Install dependencies
run: |
corepack enable
yarn install --no-immutable
cd src/webui/FE && npm install
- name: Build
run: |
npm run build
npm run build-webui
- name: Build PR LLBot image
run: docker build -f docker/Dockerfile.test -t llbot-pr-test .
- name: Write configs
run: |
mkdir -p workspace1/data workspace2/data
cat > workspace1/data/config_1577491075.json << 'CONF'
{
"ob11": {
"enable": true,
"connect": [
{ "type": "http", "enable": true, "host": "", "port": 53000, "token": "123", "messageFormat": "array", "reportSelfMessage": false, "reportOfflineMessage": false, "debug": false }
]
},
"satori": { "enable": false },
"milky": { "enable": false },
"webui": { "enable": false }
}
CONF
cat > workspace2/data/config_412805684.json << 'CONF'
{
"ob11": {
"enable": true,
"connect": [
{ "type": "http", "enable": true, "host": "", "port": 53001, "token": "123", "messageFormat": "array", "reportSelfMessage": false, "reportOfflineMessage": false, "debug": false }
]
},
"satori": { "enable": false },
"milky": { "enable": false },
"webui": { "enable": false }
}
CONF
- name: Restart PMHQ with workspace mount
run: |
docker stop pmhq1 pmhq2
docker rm -f pmhq1-test pmhq2-test 2>/dev/null || true
docker run -d --name pmhq1-test \
--network llbot-test_test_network \
--privileged \
-e ENABLE_HEADLESS=false \
-e AUTO_LOGIN_QQ=1577491075 \
-v llbot-test_qq_volume1:/root/.config/QQ \
-v ${{ github.workspace }}:/app/workspace:rw \
-v ${{ github.workspace }}/workspace1/data:/app/llbot/data:rw \
linyuchen/pmhq:latest
docker run -d --name pmhq2-test \
--network llbot-test_test_network \
--privileged \
-e ENABLE_HEADLESS=false \
-e AUTO_LOGIN_QQ=412805684 \
-v llbot-test_qq_volume2:/root/.config/QQ \
-v ${{ github.workspace }}:/app/workspace:rw \
-v ${{ github.workspace }}/workspace2/data:/app/llbot/data:rw \
linyuchen/pmhq:latest
echo "Waiting for PMHQ to be ready..."
for i in $(seq 1 30); do
H1=$(docker exec pmhq1-test curl -sf http://localhost:13000/health 2>/dev/null || true)
H2=$(docker exec pmhq2-test curl -sf http://localhost:13000/health 2>/dev/null || true)
[ -n "$H1" ] && [ -n "$H2" ] && echo "Both PMHQ ready" && break
echo "Waiting for PMHQ... ($i/30)"
sleep 5
done
- name: Start PR LLBot containers
run: |
docker rm -f llbot-pr1 llbot-pr2 2>/dev/null || true
docker run -d --name llbot-pr1 \
--network llbot-test_test_network \
-e pmhq_host=pmhq1-test \
-e pmhq_port=13000 \
-e WEBUI_PORT=3091 \
-v llbot-test_qq_volume1:/root/.config/QQ \
-v ${{ github.workspace }}/workspace1/data:/app/llbot/data:rw \
-v ${{ github.workspace }}:/app/workspace:ro \
llbot-pr-test
docker run -d --name llbot-pr2 \
--network llbot-test_test_network \
-e pmhq_host=pmhq2-test \
-e pmhq_port=13000 \
-e WEBUI_PORT=3092 \
-v llbot-test_qq_volume2:/root/.config/QQ \
-v ${{ github.workspace }}/workspace2/data:/app/llbot/data:rw \
-v ${{ github.workspace }}:/app/workspace:ro \
llbot-pr-test
- name: Wait for LLBot ready
run: |
for i in $(seq 1 60); do
R1=$(docker exec llbot-pr1 curl -sf http://127.0.0.1:53000/get_login_info -H "Authorization: Bearer 123" 2>/dev/null || true)
R2=$(docker exec llbot-pr2 curl -sf http://127.0.0.1:53001/get_login_info -H "Authorization: Bearer 123" 2>/dev/null || true)
[ -n "$R1" ] && [ -n "$R2" ] && echo "Both LLBot instances ready" && break
echo "Waiting... ($i/60)"
sleep 5
done
- name: Write test config
run: |
cat > ${{ github.workspace }}/test/onebot11-api-test/config/test.config.json << 'CONF'
{
"accounts": {
"primary": {
"host": "http://llbot-pr1:53000",
"apiKey": "123",
"protocol": "http",
"user_id": "1577491075"
},
"secondary": {
"host": "http://llbot-pr2:53001",
"apiKey": "123",
"protocol": "http",
"user_id": "412805684"
}
},
"test_group_id": "1098066573",
"timeout": 30000,
"retryAttempts": 3
}
CONF
- name: Run e2e tests in Docker
run: |
docker run --rm --name e2e-runner \
--network llbot-test_test_network \
-v ${{ github.workspace }}:/app/workspace:rw \
-w /app/workspace/test/onebot11-api-test \
node:24-alpine3.23 sh -c "npm install && npm test"
continue-on-error: true
- name: Show test summary
if: always()
run: |
if [ -f test/onebot11-api-test/test-report.html ]; then
echo "Test report generated"
else
echo "No test report found"
fi
- name: Stop test containers and restore PMHQ
if: always()
run: |
echo "=== LLBot1 logs ===" && docker logs llbot-pr1 2>&1 | tail -30 || true
echo "=== LLBot2 logs ===" && docker logs llbot-pr2 2>&1 | tail -30 || true
docker rm -f llbot-pr1 llbot-pr2 pmhq1-test pmhq2-test 2>/dev/null || true
docker start pmhq1 pmhq2 2>/dev/null || true
sudo rm -rf ${{ github.workspace }}/workspace1 ${{ github.workspace }}/workspace2
- name: Upload test report
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-test-report
path: test/onebot11-api-test/test-report.html
retention-days: 30
report:
name: Test Report
runs-on: ubuntu-latest
needs: [unit-test, e2e-test]
if: always() && github.event_name == 'pull_request'
permissions:
pull-requests: write
steps:
- name: Generate report comment
uses: actions/github-script@v7
with:
script: |
const jobs = {
'unit-test': '${{ needs.unit-test.result }}',
'e2e-test': '${{ needs.e2e-test.result }}',
};
const icons = { success: '✅', failure: '❌', skipped: '⏭️', cancelled: '🚫' };
const lines = Object.entries(jobs).map(([name, result]) =>
`| ${name} | ${icons[result] || '❓'} ${result} |`
);
const allPassed = Object.values(jobs).every(r => r === 'success' || r === 'skipped');
const summary = allPassed ? '✅ All tests passed' : '❌ Some tests failed';
const body = [
`## Test Report`,
``,
`| Job | Status |`,
`|-----|--------|`,
...lines,
``,
summary,
].join('\n');
// Find existing comment
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const botComment = comments.find(c =>
c.user.type === 'Bot' && c.body.includes('## Test Report')
);
if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
}