Appearance
Testing & Deployment
Comprehensive guide to testing strategies, deployment processes, and maintaining plugins across the Motanamy platform.
Testing Strategies
Testing Pyramid
Follow a balanced testing approach:
End-to-End Tests (10-20%)
↕
Integration Tests (20-30%)
↕
Unit Tests (40-60%)
↕
Static Analysis (10-20%)Unit Testing
Test individual functions and modules in isolation.
Setting Up Testing Framework
javascript
// test/setup.js
import { expect } from 'chai';
import sinon from 'sinon';
import { PluginTestHarness } from '@motanamy/testing';
global.expect = expect;
global.sinon = sinon;
// Test harness for plugin testing
global.harness = new PluginTestHarness();Writing Unit Tests
javascript
// test/my-plugin.test.js
import MyPlugin from '../src/MyPlugin.js';
describe('MyPlugin', () => {
let plugin;
let mockAPI;
beforeEach(() => {
plugin = new MyPlugin();
mockAPI = sinon.mock(plugin.api);
});
afterEach(() => {
mockAPI.restore();
});
describe('onLoad()', () => {
it('should register hooks successfully', async () => {
const registerHookSpy = sinon.spy(plugin, 'registerHook');
await plugin.onLoad();
expect(registerHookSpy.calledWith('onUserLogin')).to.be.true;
expect(registerHookSpy.calledWith('onDataUpdate')).to.be.true;
});
it('should initialize plugin state', async () => {
await plugin.onLoad();
expect(plugin.initialized).to.be.true;
expect(plugin.version).to.equal('1.0.0');
});
});
describe('handleUserLogin()', () => {
it('should process user login event', () => {
const user = { id: 1, name: 'Test User', role: 'admin' };
const logSpy = sinon.spy(plugin.logger, 'info');
plugin.handleUserLogin(user);
expect(logSpy.calledWith('User logged in: Test User')).to.be.true;
});
it('should handle login errors gracefully', () => {
const invalidUser = null;
expect(() => plugin.handleUserLogin(invalidUser)).to.not.throw();
});
});
});Running Unit Tests
bash
# Run all tests
npm test
# Run specific test file
npm test -- test/my-plugin.test.js
# Run with coverage
npm run test:coverage
# Watch mode for development
npm run test:watchIntegration Testing
Test how your plugin interacts with the Motanamy platform.
Platform Simulation
javascript
// test/integration/platform.test.js
import { PlatformSimulator } from '@motanamy/testing';
import MyPlugin from '../src/MyPlugin.js';
describe('Platform Integration', () => {
let simulator;
let plugin;
beforeEach(async () => {
simulator = new PlatformSimulator({
platform: 'ea',
version: '1.0.0'
});
plugin = new MyPlugin();
await simulator.loadPlugin(plugin);
});
afterEach(async () => {
await simulator.cleanup();
});
it('should handle user login workflow', async () => {
const user = { id: 1, name: 'John Doe', email: 'john@example.com' };
// Simulate user login
await simulator.triggerEvent('onUserLogin', user);
// Verify plugin response
const notifications = simulator.getNotifications();
expect(notifications).to.have.lengthOf(1);
expect(notifications[0].message).to.include('Welcome John Doe');
});
it('should integrate with API', async () => {
// Mock API response
simulator.mockAPI('/users', { users: [{ id: 1, name: 'User' }] });
// Trigger API call
await simulator.triggerEvent('onDataSync');
// Verify API was called
const apiCalls = simulator.getAPICalls();
expect(apiCalls).to.have.lengthOf(1);
expect(apiCalls[0].endpoint).to.equal('/users');
});
});Cross-Platform Testing
javascript
// test/integration/cross-platform.test.js
import { CrossPlatformTester } from '@motanamy/testing';
describe('Cross-Platform Compatibility', () => {
const platforms = ['ea', 'sa', 'os'];
const versions = ['1.0.0', '1.1.0', '2.0.0'];
platforms.forEach(platform => {
versions.forEach(version => {
describe(`${platform} ${version}`, () => {
let tester;
beforeEach(() => {
tester = new CrossPlatformTester(platform, version);
});
it('should load plugin successfully', async () => {
const result = await tester.loadPlugin('./dist/my-plugin.motapkg');
expect(result.success).to.be.true;
});
it('should execute basic functionality', async () => {
await tester.loadPlugin('./dist/my-plugin.motapkg');
const result = await tester.executeTest('basic-functionality');
expect(result.passed).to.be.true;
});
});
});
});
});End-to-End Testing
Test complete user workflows from start to finish.
E2E Test Setup
javascript
// test/e2e/user-workflow.test.js
import { E2ETester, UserSimulator } from '@motanamy/testing';
describe('User Workflow E2E', () => {
let tester;
let user;
beforeEach(async () => {
tester = new E2ETester({
platform: 'ea',
headless: true
});
user = new UserSimulator({
name: 'Test User',
email: 'test@example.com'
});
await tester.start();
await tester.loadPlugin('./dist/my-plugin.motapkg');
});
afterEach(async () => {
await tester.stop();
});
it('should complete full user registration workflow', async () => {
// Navigate to registration page
await tester.navigate('/register');
// Fill registration form
await tester.fillForm({
name: user.name,
email: user.email,
password: 'testpass123'
});
// Submit form
await tester.click('register-button');
// Verify success
await tester.waitForText('Registration successful');
// Check database
const dbUser = await tester.queryDatabase('SELECT * FROM users WHERE email = ?', [user.email]);
expect(dbUser).to.have.lengthOf(1);
});
it('should handle plugin-enhanced workflow', async () => {
// Login user
await user.login(tester);
// Navigate to plugin feature
await tester.click('plugin-menu-item');
// Interact with plugin UI
await tester.fillForm({ input: 'test data' });
await tester.click('process-button');
// Verify plugin functionality
await tester.waitForText('Data processed successfully');
// Check plugin logs
const logs = await tester.getPluginLogs();
expect(logs).to.include('Processing completed');
});
});Performance Testing
Ensure your plugin performs well under various conditions.
Load Testing
javascript
// test/performance/load.test.js
import { LoadTester } from '@motanamy/testing';
describe('Performance Testing', () => {
let loadTester;
beforeEach(() => {
loadTester = new LoadTester({
duration: '30s',
concurrency: 10,
rampUp: '10s'
});
});
it('should handle concurrent user load', async () => {
const results = await loadTester.runScenario({
name: 'concurrent-users',
setup: async () => {
await loadTester.loadPlugin('./dist/my-plugin.motapkg');
},
scenario: async (user) => {
// Simulate user actions
await user.login();
await user.performAction('heavy-computation');
await user.logout();
}
});
// Assert performance metrics
expect(results.avgResponseTime).to.be.below(1000); // < 1 second
expect(results.errorRate).to.be.below(0.05); // < 5% errors
expect(results.throughput).to.be.above(50); // > 50 requests/second
});
it('should maintain performance under memory pressure', async () => {
const results = await loadTester.runMemoryTest({
duration: '5m',
memoryLimit: '512MB',
scenario: async () => {
// Memory-intensive operations
for (let i = 0; i < 1000; i++) {
await loadTester.performMemoryOperation();
}
}
});
expect(results.memoryLeak).to.be.false;
expect(results.gcPressure).to.be.below(0.8);
});
});Security Testing
Verify your plugin doesn't introduce security vulnerabilities.
Security Test Suite
javascript
// test/security/security.test.js
import { SecurityTester } from '@motanamy/testing';
describe('Security Testing', () => {
let securityTester;
beforeEach(async () => {
securityTester = new SecurityTester();
await securityTester.loadPlugin('./dist/my-plugin.motapkg');
});
it('should prevent SQL injection', async () => {
const maliciousInput = "'; DROP TABLE users; --";
const result = await securityTester.testSQLInjection({
endpoint: '/api/search',
payload: { query: maliciousInput }
});
expect(result.vulnerable).to.be.false;
});
it('should validate input properly', async () => {
const testCases = [
{ input: '<script>alert("xss")</script>', shouldBlock: true },
{ input: 'normal text', shouldBlock: false },
{ input: '../../../etc/passwd', shouldBlock: true }
];
for (const testCase of testCases) {
const result = await securityTester.testInputValidation(testCase.input);
expect(result.blocked).to.equal(testCase.shouldBlock);
}
});
it('should handle authentication correctly', async () => {
// Test unauthorized access
const unauthorized = await securityTester.testUnauthorizedAccess('/admin');
expect(unauthorized.blocked).to.be.true;
// Test authorized access
await securityTester.authenticate('admin');
const authorized = await securityTester.testAuthorizedAccess('/admin');
expect(authorized.allowed).to.be.true;
});
});Continuous Integration
CI/CD Pipeline Setup
GitHub Actions Example
yaml
# .github/workflows/ci.yml
name: CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x, 20.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linting
run: npm run lint
- name: Run unit tests
run: npm run test:unit
- name: Run integration tests
run: npm run test:integration
- name: Run security scan
run: npm run security-scan
- name: Build plugin
run: npm run build
- name: Run E2E tests
run: npm run test:e2e
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to staging
run: npm run deploy:staging
- name: Run smoke tests
run: npm run test:smoke
- name: Deploy to production
run: npm run deploy:productionAutomated Testing Scripts
bash
# package.json
{
"scripts": {
"test": "npm run test:unit && npm run test:integration",
"test:unit": "mocha test/unit/**/*.test.js",
"test:integration": "mocha test/integration/**/*.test.js",
"test:e2e": "cypress run",
"test:security": "npm audit && motanamy security-scan",
"test:performance": "artillery run test/performance/load.yml",
"test:smoke": "mocha test/smoke/**/*.test.js",
"lint": "eslint src/**/*.js",
"build": "webpack --mode production",
"deploy:staging": "motanamy deploy --env staging",
"deploy:production": "motanamy deploy --env production"
}
}Deployment Strategies
Staging Environment
Test your plugin in a staging environment before production deployment.
bash
# Deploy to staging
motanamy deploy my-plugin.motapkg --env staging --version 1.0.0-beta.1
# Run staging tests
npm run test:staging
# Promote to production
motanamy promote my-plugin --from staging --to productionBlue-Green Deployment
Minimize downtime with blue-green deployment strategy.
bash
# Deploy to blue environment
motanamy deploy my-plugin.motapkg --env blue --version 1.1.0
# Test blue environment
npm run test:blue
# Switch traffic to blue
motanamy switch-traffic --to blue
# Keep green as rollback option
# If issues found, switch back
motanamy switch-traffic --to greenCanary Deployment
Gradually roll out new versions to subsets of users.
bash
# Deploy to 10% of users
motanamy canary-deploy my-plugin.motapkg --percentage 10
# Monitor metrics
motanamy monitor my-plugin --metrics error-rate,performance
# Gradually increase rollout
motanamy canary-deploy my-plugin.motapkg --percentage 25
motanamy canary-deploy my-plugin.motapkg --percentage 50
motanamy canary-deploy my-plugin.motapkg --percentage 100Rollback Procedures
bash
# Quick rollback to previous version
motanamy rollback my-plugin --to-version 1.0.0
# Rollback with data migration
motanamy rollback my-plugin --to-version 1.0.0 --migrate-data
# Emergency rollback (immediate)
motanamy emergency-rollback my-pluginMonitoring and Maintenance
Health Checks
Implement health checks for your plugin.
javascript
// health.js
export async function healthCheck() {
const checks = {
database: await checkDatabaseConnection(),
api: await checkAPIConnectivity(),
memory: await checkMemoryUsage(),
disk: await checkDiskSpace()
};
const healthy = Object.values(checks).every(check => check.status === 'healthy');
return {
status: healthy ? 'healthy' : 'unhealthy',
timestamp: new Date().toISOString(),
checks
};
}
async function checkDatabaseConnection() {
try {
await db.query('SELECT 1');
return { status: 'healthy' };
} catch (error) {
return { status: 'unhealthy', error: error.message };
}
}Logging and Monitoring
javascript
// monitoring.js
import { Logger, Metrics } from '@motanamy/sdk';
export class PluginMonitor {
constructor() {
this.metrics = new Metrics('my-plugin');
this.logger = new Logger('monitor');
}
async recordMetric(name, value, tags = {}) {
await this.metrics.record(name, value, tags);
}
async logEvent(level, message, context = {}) {
this.logger[level](message, context);
}
async monitorPerformance(operation, fn) {
const startTime = Date.now();
try {
const result = await fn();
const duration = Date.now() - startTime;
await this.recordMetric('operation_duration', duration, {
operation,
status: 'success'
});
return result;
} catch (error) {
const duration = Date.now() - startTime;
await this.recordMetric('operation_duration', duration, {
operation,
status: 'error'
});
await this.logEvent('error', `Operation ${operation} failed`, {
error: error.message,
duration
});
throw error;
}
}
}Alerting
Set up alerts for critical issues.
javascript
// alerts.js
import { AlertManager } from '@motanamy/sdk';
export class PluginAlerts {
constructor() {
this.alertManager = new AlertManager();
}
async setupAlerts() {
// Error rate alert
await this.alertManager.createAlert({
name: 'high-error-rate',
condition: 'error_rate > 0.05',
duration: '5m',
severity: 'critical',
channels: ['email', 'slack']
});
// Performance degradation alert
await this.alertManager.createAlert({
name: 'slow-response-time',
condition: 'avg_response_time > 2000',
duration: '10m',
severity: 'warning',
channels: ['email']
});
// Memory usage alert
await this.alertManager.createAlert({
name: 'high-memory-usage',
condition: 'memory_usage > 80',
duration: '5m',
severity: 'warning',
channels: ['email']
});
}
async sendAlert(alertName, details) {
await this.alertManager.trigger(alertName, details);
}
}Version Management
Semantic Versioning Strategy
javascript
// version.js
export class VersionManager {
constructor(currentVersion) {
this.current = this.parseVersion(currentVersion);
}
parseVersion(version) {
const [major, minor, patch] = version.split('.').map(Number);
return { major, minor, patch };
}
bump(type) {
const newVersion = { ...this.current };
switch (type) {
case 'major':
newVersion.major++;
newVersion.minor = 0;
newVersion.patch = 0;
break;
case 'minor':
newVersion.minor++;
newVersion.patch = 0;
break;
case 'patch':
newVersion.patch++;
break;
}
return `${newVersion.major}.${newVersion.minor}.${newVersion.patch}`;
}
isCompatible(version) {
const other = this.parseVersion(version);
return other.major === this.current.major;
}
}Update Mechanisms
javascript
// updates.js
export class UpdateManager {
constructor() {
this.checkInterval = 24 * 60 * 60 * 1000; // 24 hours
}
async checkForUpdates() {
try {
const latestVersion = await this.fetchLatestVersion();
const currentVersion = await this.getCurrentVersion();
if (this.isNewerVersion(latestVersion, currentVersion)) {
await this.notifyUserOfUpdate(latestVersion);
}
} catch (error) {
console.error('Failed to check for updates:', error);
}
}
async applyUpdate(newVersion) {
// Download update
await this.downloadUpdate(newVersion);
// Backup current version
await this.createBackup();
// Apply update
await this.installUpdate(newVersion);
// Verify installation
await this.verifyUpdate(newVersion);
// Restart if necessary
await this.restartPlugin();
}
}Troubleshooting Deployment Issues
Common Deployment Problems
Plugin Won't Install
- Check platform compatibility
- Verify dependencies are available
- Ensure sufficient permissions
Runtime Errors
- Check error logs
- Verify configuration
- Test in isolation
Performance Issues
- Profile resource usage
- Check for memory leaks
- Optimize database queries
Debugging Tools
bash
# Enable debug logging
motanamy debug my-plugin --level verbose
# Profile performance
motanamy profile my-plugin --duration 60s
# Inspect plugin state
motanamy inspect my-plugin
# Simulate platform events
motanamy simulate my-plugin --event onUserLogin --data '{"userId": 123}'Best Practices
Testing Best Practices
- Test Early and Often: Integrate testing into development workflow
- Automate Everything: Use CI/CD for automated testing
- Test Realistic Scenarios: Use production-like data and environments
- Monitor Test Coverage: Aim for >80% code coverage
- Test Failure Scenarios: Don't just test happy paths
Deployment Best Practices
- Zero-Downtime Deployments: Use blue-green or canary strategies
- Automated Rollbacks: Have rollback procedures ready
- Gradual Rollouts: Start with small user groups
- Monitoring: Monitor key metrics post-deployment
- Documentation: Document deployment procedures
Maintenance Best Practices
- Regular Updates: Keep dependencies and platform SDK updated
- Security Patches: Address vulnerabilities promptly
- Performance Monitoring: Monitor and optimize performance
- User Feedback: Listen to user reports and feedback
- Version Planning: Plan version releases and deprecation schedules
Resources
- Testing Framework Documentation
- CI/CD Best Practices
- Deployment Strategies
- Monitoring Guide
- Support Center
Thorough testing and reliable deployment are crucial for plugin success. Following these practices ensures your plugins are robust, secure, and provide excellent user experiences across the Motanamy platform.