onairos
Version:
The Onairos Library is a collection of functions that enable Applications to connect and communicate data with Onairos Identities via User Authorization. Integration for developers is seamless, simple and effective for all applications. LLM SDK capabiliti
638 lines (537 loc) • 21.5 kB
JavaScript
import request from 'supertest';
import express from 'express';
import { jest } from '@jest/globals';
import validationRoutes from '../../routes/validation.js';
// Mock dependencies
jest.mock('../../utils/tokenManager.js');
jest.mock('../../utils/connectionHealth.js');
jest.mock('../../utils/databaseUtils.js');
jest.mock('../../middleware/unifiedApiKeyAuth.js');
jest.mock('../../config/sdk-config.js');
const app = express();
app.use(express.json());
// Mock middleware
const mockAuthenticateApiKey = (req, res, next) => {
req.apiKeyInfo = {
keyId: 'test_key_id',
permissions: ['oauth:*']
};
next();
};
// Mock auth middleware
jest.mock('../../middleware/unifiedApiKeyAuth.js', () => ({
authenticateApiKey: mockAuthenticateApiKey
}));
app.use('/validation', validationRoutes);
describe('Validation Routes', () => {
let mockDbUtils;
let mockHealthMonitor;
let mockTokenManager;
let mockSdkConfig;
beforeEach(() => {
// Reset mocks
jest.clearAllMocks();
// Mock database utils
mockDbUtils = {
getUserWithSmartLookup: jest.fn(),
getUserConnections: jest.fn(),
updateUserConnection: jest.fn()
};
// Mock health monitor
mockHealthMonitor = {
checkConnectionHealth: jest.fn()
};
// Mock token manager
mockTokenManager = {
refreshTokenIfNeeded: jest.fn()
};
// Mock SDK config
mockSdkConfig = {
platforms: {
youtube: { enabled: true },
linkedin: { enabled: true },
reddit: { enabled: true }
},
features: {
dualAuthentication: true,
connectionHealthMonitoring: true
}
};
// Apply mocks
const { DatabaseUtils } = await import('../../utils/databaseUtils.js');
const { ConnectionHealthMonitor } = await import('../../utils/connectionHealth.js');
const { TokenManager } = await import('../../utils/tokenManager.js');
const { sdkConfig } = await import('../../config/sdk-config.js');
DatabaseUtils.prototype.getUserWithSmartLookup = mockDbUtils.getUserWithSmartLookup;
DatabaseUtils.prototype.getUserConnections = mockDbUtils.getUserConnections;
DatabaseUtils.prototype.updateUserConnection = mockDbUtils.updateUserConnection;
ConnectionHealthMonitor.prototype.checkConnectionHealth = mockHealthMonitor.checkConnectionHealth;
TokenManager.prototype.refreshTokenIfNeeded = mockTokenManager.refreshTokenIfNeeded;
Object.assign(sdkConfig, mockSdkConfig);
});
describe('GET /validation/health-check/:username', () => {
it('should return comprehensive health check for all platforms', async () => {
// Mock user lookup
mockDbUtils.getUserWithSmartLookup.mockResolvedValue({
user: {
_id: 'user_id',
userName: 'test_user'
},
userType: 'onairos'
});
// Mock user connections
mockDbUtils.getUserConnections.mockReturnValue({
youtube: {
accessToken: 'youtube_token',
hasRefreshToken: true
},
linkedin: {
accessToken: 'linkedin_token',
hasRefreshToken: false
}
});
// Mock health check results
mockHealthMonitor.checkConnectionHealth
.mockResolvedValueOnce({
status: 'healthy',
message: 'YouTube connection is healthy'
})
.mockResolvedValueOnce({
status: 'expired_refreshable',
message: 'LinkedIn token is expired but can be refreshed'
})
.mockResolvedValueOnce({
status: 'not_connected',
message: 'Reddit is not connected'
});
const response = await request(app)
.get('/validation/health-check/test_user');
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.username).toBe('test_user');
expect(response.body.userType).toBe('onairos');
expect(response.body.summary.connectedPlatforms).toBe(2);
expect(response.body.summary.healthyPlatforms).toBe(1);
expect(response.body.platforms).toHaveProperty('youtube');
expect(response.body.platforms).toHaveProperty('linkedin');
expect(response.body.platforms).toHaveProperty('reddit');
expect(response.body.recommendations).toBeInstanceOf(Array);
});
it('should return error for user not found', async () => {
mockDbUtils.getUserWithSmartLookup.mockResolvedValue({
user: null,
userType: null
});
const response = await request(app)
.get('/validation/health-check/nonexistent_user');
expect(response.status).toBe(404);
expect(response.body.success).toBe(false);
expect(response.body.error).toBe('User not found');
expect(response.body.code).toBe('USER_NOT_FOUND');
});
it('should calculate overall health score correctly', async () => {
mockDbUtils.getUserWithSmartLookup.mockResolvedValue({
user: {
_id: 'user_id',
userName: 'test_user'
},
userType: 'onairos'
});
mockDbUtils.getUserConnections.mockReturnValue({
youtube: { accessToken: 'token' },
linkedin: { accessToken: 'token' }
});
// Mock two healthy connections
mockHealthMonitor.checkConnectionHealth
.mockResolvedValueOnce({ status: 'healthy' })
.mockResolvedValueOnce({ status: 'healthy' })
.mockResolvedValueOnce({ status: 'not_connected' });
const response = await request(app)
.get('/validation/health-check/test_user');
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.summary.overallScore).toBe(100);
expect(response.body.summary.overallStatus).toBe('healthy');
});
it('should generate appropriate recommendations', async () => {
mockDbUtils.getUserWithSmartLookup.mockResolvedValue({
user: {
_id: 'user_id',
userName: 'test_user'
},
userType: 'onairos'
});
mockDbUtils.getUserConnections.mockReturnValue({
youtube: { accessToken: 'token' }
});
mockHealthMonitor.checkConnectionHealth
.mockResolvedValueOnce({ status: 'expired_refreshable' })
.mockResolvedValueOnce({ status: 'not_connected' })
.mockResolvedValueOnce({ status: 'not_connected' });
const response = await request(app)
.get('/validation/health-check/test_user');
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.recommendations).toContainEqual(
expect.objectContaining({
type: 'token_refresh_available',
platform: 'youtube'
})
);
expect(response.body.recommendations).toContainEqual(
expect.objectContaining({
type: 'expand_connections'
})
);
});
it('should handle platform health check errors', async () => {
mockDbUtils.getUserWithSmartLookup.mockResolvedValue({
user: {
_id: 'user_id',
userName: 'test_user'
},
userType: 'onairos'
});
mockDbUtils.getUserConnections.mockReturnValue({
youtube: { accessToken: 'token' }
});
// Mock health check error
mockHealthMonitor.checkConnectionHealth
.mockRejectedValueOnce(new Error('Health check failed'))
.mockResolvedValueOnce({ status: 'not_connected' })
.mockResolvedValueOnce({ status: 'not_connected' });
const response = await request(app)
.get('/validation/health-check/test_user');
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.platforms.youtube.status).toBe('error');
expect(response.body.platforms.youtube.error).toBe('Health check failed');
});
});
describe('POST /validation/repair-connections/:username', () => {
it('should repair connections successfully', async () => {
mockDbUtils.getUserWithSmartLookup.mockResolvedValue({
user: {
_id: 'user_id',
userName: 'test_user'
},
userType: 'onairos'
});
mockDbUtils.getUserConnections.mockReturnValue({
youtube: {
accessToken: 'youtube_token',
hasRefreshToken: true
},
linkedin: {
accessToken: 'linkedin_token',
hasRefreshToken: false
}
});
// Mock health check results
mockHealthMonitor.checkConnectionHealth
.mockResolvedValueOnce({ status: 'expired_refreshable' })
.mockResolvedValueOnce({ status: 'expired_no_refresh' })
.mockResolvedValueOnce({ status: 'healthy' });
// Mock token refresh
mockTokenManager.refreshTokenIfNeeded.mockResolvedValue({
success: true,
message: 'Token refreshed successfully',
refreshed: true
});
const response = await request(app)
.post('/validation/repair-connections/test_user')
.send({
platforms: ['youtube', 'linkedin']
});
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.summary.successfulRepairs).toBe(1);
expect(response.body.summary.failedRepairs).toBe(1);
expect(response.body.successfulRepairs).toContain('youtube');
expect(response.body.failedRepairs).toContain('linkedin');
});
it('should repair all connected platforms when no specific platforms provided', async () => {
mockDbUtils.getUserWithSmartLookup.mockResolvedValue({
user: {
_id: 'user_id',
userName: 'test_user'
},
userType: 'onairos'
});
mockDbUtils.getUserConnections.mockReturnValue({
youtube: { accessToken: 'token' },
linkedin: { accessToken: 'token' }
});
mockHealthMonitor.checkConnectionHealth
.mockResolvedValue({ status: 'healthy' });
const response = await request(app)
.post('/validation/repair-connections/test_user')
.send({});
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.summary.totalPlatforms).toBe(2);
expect(response.body.repairResults).toHaveProperty('youtube');
expect(response.body.repairResults).toHaveProperty('linkedin');
});
it('should handle platforms that are already healthy', async () => {
mockDbUtils.getUserWithSmartLookup.mockResolvedValue({
user: {
_id: 'user_id',
userName: 'test_user'
},
userType: 'onairos'
});
mockDbUtils.getUserConnections.mockReturnValue({
youtube: { accessToken: 'token' }
});
mockHealthMonitor.checkConnectionHealth
.mockResolvedValue({ status: 'healthy' });
const response = await request(app)
.post('/validation/repair-connections/test_user')
.send({
platforms: ['youtube']
});
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.repairResults.youtube.message).toBe('Platform is already healthy, no repair needed');
expect(response.body.repairResults.youtube.repairAttempted).toBe(false);
expect(response.body.successfulRepairs).toContain('youtube');
});
it('should handle token refresh failures', async () => {
mockDbUtils.getUserWithSmartLookup.mockResolvedValue({
user: {
_id: 'user_id',
userName: 'test_user'
},
userType: 'onairos'
});
mockDbUtils.getUserConnections.mockReturnValue({
youtube: { accessToken: 'token' }
});
mockHealthMonitor.checkConnectionHealth
.mockResolvedValue({ status: 'expired_refreshable' });
mockTokenManager.refreshTokenIfNeeded.mockResolvedValue({
success: false,
error: 'Refresh token expired'
});
const response = await request(app)
.post('/validation/repair-connections/test_user')
.send({
platforms: ['youtube']
});
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.repairResults.youtube.repairSuccess).toBe(false);
expect(response.body.repairResults.youtube.error).toBe('Refresh token expired');
expect(response.body.failedRepairs).toContain('youtube');
});
it('should generate recommendations for failed repairs', async () => {
mockDbUtils.getUserWithSmartLookup.mockResolvedValue({
user: {
_id: 'user_id',
userName: 'test_user'
},
userType: 'onairos'
});
mockDbUtils.getUserConnections.mockReturnValue({
youtube: { accessToken: 'token' }
});
mockHealthMonitor.checkConnectionHealth
.mockResolvedValue({ status: 'expired_no_refresh' });
const response = await request(app)
.post('/validation/repair-connections/test_user')
.send({
platforms: ['youtube']
});
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.recommendations).toContainEqual(
expect.objectContaining({
type: 'manual_reconnection_required',
platform: 'youtube'
})
);
});
});
describe('GET /validation/migration-status/:username', () => {
it('should return migration status for user', async () => {
mockDbUtils.getUserWithSmartLookup.mockResolvedValue({
user: {
_id: 'user_id',
userName: 'test_user'
},
userType: 'onairos'
});
mockDbUtils.getUserConnections.mockReturnValue({
youtube: {
accessToken: 'token',
refreshToken: 'refresh_token',
lastValidated: new Date()
},
linkedin: {
accessToken: 'token',
refreshToken: null,
lastValidated: new Date(Date.now() - 35 * 24 * 60 * 60 * 1000) // 35 days ago
}
});
const response = await request(app)
.get('/validation/migration-status/test_user');
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.migrationStatus.connectedPlatforms).toBe(2);
expect(response.body.migrationStatus.platformsNeedingMigration).toBe(1);
expect(response.body.migrationNeeded).toContain('linkedin');
expect(response.body.platformStatuses.youtube.needsUpgrade).toBe(false);
expect(response.body.platformStatuses.linkedin.needsUpgrade).toBe(true);
});
it('should detect legacy connections', async () => {
mockDbUtils.getUserWithSmartLookup.mockResolvedValue({
user: {
_id: 'user_id',
userName: 'test_user'
},
userType: 'onairos'
});
const oldDate = new Date(Date.now() - 35 * 24 * 60 * 60 * 1000); // 35 days ago
mockDbUtils.getUserConnections.mockReturnValue({
youtube: {
accessToken: 'token',
refreshToken: 'refresh_token',
lastValidated: oldDate
}
});
const response = await request(app)
.get('/validation/migration-status/test_user');
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.platformStatuses.youtube.isLegacyConnection).toBe(true);
expect(response.body.recommendations).toContainEqual(
expect.objectContaining({
type: 'legacy_connection',
platform: 'youtube'
})
);
});
it('should calculate migration progress correctly', async () => {
mockDbUtils.getUserWithSmartLookup.mockResolvedValue({
user: {
_id: 'user_id',
userName: 'test_user'
},
userType: 'onairos'
});
mockDbUtils.getUserConnections.mockReturnValue({
youtube: {
accessToken: 'token',
refreshToken: 'refresh_token',
lastValidated: new Date()
},
linkedin: {
accessToken: 'token',
refreshToken: 'refresh_token',
lastValidated: new Date()
}
});
const response = await request(app)
.get('/validation/migration-status/test_user');
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.migrationStatus.status).toBe('completed');
expect(response.body.migrationStatus.migrationProgress).toBe(100);
expect(response.body.migrationNeeded).toHaveLength(0);
});
});
describe('GET /validation/system-health', () => {
it('should return system health status', async () => {
const response = await request(app)
.get('/validation/system-health');
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.overallHealth).toBeDefined();
expect(response.body.healthChecks).toHaveProperty('configuration');
expect(response.body.healthChecks).toHaveProperty('database');
expect(response.body.healthChecks).toHaveProperty('platforms');
expect(response.body.healthChecks).toHaveProperty('authentication');
expect(response.body.systemInfo).toBeDefined();
});
it('should calculate overall health score', async () => {
const response = await request(app)
.get('/validation/system-health');
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.overallHealth.score).toBeGreaterThanOrEqual(0);
expect(response.body.overallHealth.score).toBeLessThanOrEqual(100);
expect(response.body.overallHealth.status).toMatch(/healthy|warning|error/);
});
it('should include system information', async () => {
const response = await request(app)
.get('/validation/system-health');
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.systemInfo).toHaveProperty('uptime');
expect(response.body.systemInfo).toHaveProperty('nodeVersion');
expect(response.body.systemInfo).toHaveProperty('environment');
});
});
describe('Error handling', () => {
it('should handle database errors gracefully', async () => {
mockDbUtils.getUserWithSmartLookup.mockRejectedValue(
new Error('Database connection failed')
);
const response = await request(app)
.get('/validation/health-check/test_user');
expect(response.status).toBe(500);
expect(response.body.success).toBe(false);
expect(response.body.error).toBe('Failed to perform health check');
expect(response.body.code).toBe('HEALTH_CHECK_ERROR');
});
it('should handle repair operation errors', async () => {
mockDbUtils.getUserWithSmartLookup.mockRejectedValue(
new Error('Repair operation failed')
);
const response = await request(app)
.post('/validation/repair-connections/test_user');
expect(response.status).toBe(500);
expect(response.body.success).toBe(false);
expect(response.body.error).toBe('Failed to repair connections');
expect(response.body.code).toBe('REPAIR_ERROR');
});
it('should handle system health check errors', async () => {
// Mock SDK config to cause error
const originalConfig = mockSdkConfig.features;
mockSdkConfig.features = null;
const response = await request(app)
.get('/validation/system-health');
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.healthChecks.configuration.status).toBe('error');
// Restore original config
mockSdkConfig.features = originalConfig;
});
});
describe('Request validation', () => {
it('should validate username parameter', async () => {
const response = await request(app)
.get('/validation/health-check/');
expect(response.status).toBe(404);
});
it('should validate repair request body', async () => {
mockDbUtils.getUserWithSmartLookup.mockResolvedValue({
user: {
_id: 'user_id',
userName: 'test_user'
},
userType: 'onairos'
});
mockDbUtils.getUserConnections.mockReturnValue({});
const response = await request(app)
.post('/validation/repair-connections/test_user')
.send({
platforms: 'invalid_format'
});
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.summary.totalPlatforms).toBe(0);
});
});
});