| name | twinmind-ci-integration |
| description | Integrate TwinMind into CI/CD pipelines for automated testing and deployment.
Use when setting up GitHub Actions, GitLab CI, or other CI systems
with TwinMind API testing and validation.
Trigger with phrases like "twinmind ci", "twinmind github actions",
"twinmind pipeline", "automate twinmind testing".
|
| allowed-tools | Read, Write, Edit, Bash(npm:*), Grep |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
TwinMind CI Integration
Overview
Integrate TwinMind testing and validation into CI/CD pipelines.
Prerequisites
- TwinMind Pro/Enterprise API access
- CI/CD system (GitHub Actions, GitLab CI, etc.)
- Test audio samples
- Secrets management
Instructions
Step 1: GitHub Actions Workflow
name: TwinMind Integration Tests
on:
push:
branches: [main, develop]
paths:
- 'src/twinmind/**'
- 'tests/twinmind/**'
pull_request:
branches: [main]
schedule:
- cron: '0 6 * * *'
env:
NODE_VERSION: '20'
jobs:
lint-and-typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run typecheck
run: npm run typecheck
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm run test:unit
env:
TWINMIND_API_KEY: ${{ secrets.TWINMIND_API_KEY_TEST }}
integration-tests:
runs-on: ubuntu-latest
needs: [lint-and-typecheck, unit-tests]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run integration tests
run: npm run test:integration
env:
TWINMIND_API_KEY: ${{ secrets.TWINMIND_API_KEY_TEST }}
TWINMIND_WEBHOOK_SECRET: ${{ secrets.TWINMIND_WEBHOOK_SECRET }}
timeout-minutes: 10
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: coverage/
api-health-check:
runs-on: ubuntu-latest
steps:
- name: Check TwinMind API Health
run: |
RESPONSE=$(curl -s -w "\n%{http_code}" \
-H "Authorization: Bearer ${{ secrets.TWINMIND_API_KEY_TEST }}" \
https://api.twinmind.com/v1/health)
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | head -n -1)
echo "HTTP Status: $HTTP_CODE"
echo "Response: $BODY"
if [ "$HTTP_CODE" != "200" ]; then
echo "API health check failed"
exit 1
fi
transcription-smoke-test:
runs-on: ubuntu-latest
needs: [integration-tests, api-health-check]
if: github.event_name == 'schedule' || github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run transcription smoke test
run: npm run test:smoke
env:
TWINMIND_API_KEY: ${{ secrets.TWINMIND_API_KEY_TEST }}
TEST_AUDIO_URL: ${{ secrets.TEST_AUDIO_URL }}
timeout-minutes: 5
Step 2: Unit Test Setup
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { TwinMindClient } from '../../../src/twinmind/client';
describe('TwinMindClient', () => {
let client: TwinMindClient;
beforeEach(() => {
client = new TwinMindClient({
apiKey: 'test_api_key',
baseUrl: 'https://api.twinmind.com/v1',
});
});
describe('transcribe', () => {
it('should send correct request for transcription', async () => {
const mockFetch = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({
id: 'tr_123',
status: 'processing',
}),
});
global.fetch = mockFetch;
const result = await client.transcribe('https://example.com/audio.mp3');
expect(mockFetch).(
expect.(),
expect.({
: ,
: expect.({
: ,
}),
})
);
});
(, () => {
mockFetch = vi.().({
: ,
: ,
: ({ : }),
});
. = mockFetch;
(client.())
..();
});
});
(, {
(, () => {
mockFetch = vi.().({
: ,
: .({
: ,
: [{ : , : }],
}),
});
. = mockFetch;
result = client.();
(result.).();
(result.).();
});
});
});
Step 3: Integration Test Setup
import { describe, it, expect, beforeAll } from 'vitest';
import { TwinMindClient } from '../../../src/twinmind/client';
describe('TwinMind Transcription Integration', () => {
let client: TwinMindClient;
beforeAll(() => {
const apiKey = process.env.TWINMIND_API_KEY;
if (!apiKey) {
throw new Error('TWINMIND_API_KEY required for integration tests');
}
client = new TwinMindClient({ apiKey });
});
describe('API Health', () => {
it('should return healthy status', async () => {
const health = await client.healthCheck();
expect(health).toBe(true);
});
});
describe('Account', () => {
it('should return account info', async () => {
const account = await client.getAccount();
(account.).();
(account.).();
});
});
(, {
(, () => {
audioUrl = process..;
(!audioUrl) {
.();
;
}
transcript = client.(audioUrl, {
: ,
: ,
});
(transcript.).();
(transcript..).();
}, );
});
});
Step 4: Smoke Test Script
import { TwinMindClient } from '../../src/twinmind/client';
interface SmokeTestResult {
name: string;
passed: boolean;
duration: number;
error?: string;
}
async function runSmokeTests(): Promise<SmokeTestResult[]> {
const results: SmokeTestResult[] = [];
const client = new TwinMindClient({
apiKey: process.env.TWINMIND_API_KEY!,
});
const healthStart = Date.now();
try {
await client.healthCheck();
results.push({
name: 'API Health Check',
passed: true,
duration: Date.now() - healthStart,
});
} catch (error: any) {
results.push({
name: 'API Health Check',
: ,
: .() - healthStart,
: error.,
});
}
accountStart = .();
{
account = client.();
results.({
: ,
: !!account.,
: .() - accountStart,
});
} (: ) {
results.({
: ,
: ,
: .() - accountStart,
: error.,
});
}
(process..) {
transcribeStart = .();
{
transcript = client.(process.., {
: ,
});
results.({
: ,
: !!transcript. && transcript.. > ,
: .() - transcribeStart,
});
} (: ) {
results.({
: ,
: ,
: .() - transcribeStart,
: error.,
});
}
}
results;
}
() {
.();
results = ();
.();
.();
allPassed = ;
( result results) {
status = result. ? : ;
.();
(result.) {
.();
}
(!result.) allPassed = ;
}
.( + (allPassed ? : ));
process.(allPassed ? : );
}
().(.);
Step 5: GitLab CI Configuration
stages:
- lint
- test
- integration
- deploy
variables:
NODE_VERSION: "20"
.node-template: &node-template
image: node:${NODE_VERSION}
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
lint:
<<: *node-template
stage: lint
script:
- npm ci
- npm run lint
- npm run typecheck
unit-tests:
<<: *node-template
stage: test
script:
- npm ci
- npm run test:unit
coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
artifacts:
reports:
Step 6: Test Configuration
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node',
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html', 'cobertura'],
exclude: [
'node_modules/',
'tests/',
'dist/',
],
},
testTimeout: 30000,
hookTimeout: 30000,
},
});
{
"scripts": {
"test": "vitest",
"test:unit": "vitest run tests/unit",
"test:integration": "vitest run tests/integration",
"test:smoke": "ts-node tests/smoke/twinmind-smoke.ts",
"test:coverage": "vitest run --coverage"
}
}
Output
- GitHub Actions workflow
- Unit test suite
- Integration test suite
- Smoke test script
- GitLab CI configuration
Error Handling
| Issue | Cause | Solution |
|---|
| Secret not found | Not configured | Add to GitHub Secrets |
| Test timeout | Large audio file | Use short test samples |
| Rate limited | Too many CI runs | Use test account limits |
| API unavailable | TwinMind outage | Add retry logic |
Best Practices
- Use test API keys - Never use production keys in CI
- Short test audio - Use 10-30 second samples
- Cache dependencies - Speed up pipeline runs
- Timeout limits - Set reasonable timeouts
- Parallel tests - Run independent tests concurrently
- Artifact storage - Save test results and coverage
Resources
Next Steps
For deployment integration, see twinmind-deploy-integration.