puter-deploy / src /backend /test /modules /captcha /integration /extension-integration.test.js
gionuibk's picture
Upload folder using huggingface_hub
61d39e2 verified
/*
* Copyright (C) 2024-present Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { describe, it, expect, beforeEach, vi } from 'vitest';
// Mock the Context and services
const Context = {
get: vi.fn(),
};
// Mock the extension service
class ExtensionService {
constructor () {
this.extensions = new Map();
this.eventHandlers = new Map();
}
registerExtension (name, extension) {
this.extensions.set(name, extension);
}
on (event, handler) {
if ( ! this.eventHandlers.has(event) ) {
this.eventHandlers.set(event, []);
}
this.eventHandlers.get(event).push(handler);
}
async emit (event, data) {
const handlers = this.eventHandlers.get(event) || [];
for ( const handler of handlers ) {
await handler(data);
}
}
}
describe('Extension Integration with Captcha', () => {
let extensionService, captchaService, services;
beforeEach(() => {
// Reset mocks
vi.clearAllMocks();
// Create fresh instances
extensionService = new ExtensionService();
captchaService = {
enabled: true,
verifyCaptcha: vi.fn(),
};
services = {
get: vi.fn(),
};
// Configure service mocks
services.get.mockImplementation((serviceName) => {
if ( serviceName === 'extension' ) return extensionService;
if ( serviceName === 'captcha' ) return captchaService;
});
// Configure Context mock
Context.get.mockImplementation((key) => {
if ( key === 'services' ) return services;
});
});
describe('Extension Event Handling', () => {
it('should allow extensions to require captcha via event handler', async () => {
// Setup - create a test extension that requires captcha
const testExtension = {
name: 'test-extension',
onCaptchaValidate: async (event) => {
if ( event.type === 'login' && event.ip === '1.2.3.4' ) {
event.require = true;
}
},
};
// Register extension and event handler
extensionService.registerExtension(testExtension.name, testExtension);
extensionService.on('captcha.validate', testExtension.onCaptchaValidate);
// Test event emission
const eventData = {
type: 'login',
ip: '1.2.3.4',
require: false,
};
await extensionService.emit('captcha.validate', eventData);
// Assert
expect(eventData.require).toBe(true);
});
it('should allow extensions to disable captcha requirement', async () => {
// Setup - create a test extension that disables captcha
const testExtension = {
name: 'test-extension',
onCaptchaValidate: async (event) => {
if ( event.type === 'login' && event.ip === 'trusted-ip' ) {
event.require = false;
}
},
};
// Register extension and event handler
extensionService.registerExtension(testExtension.name, testExtension);
extensionService.on('captcha.validate', testExtension.onCaptchaValidate);
// Test event emission
const eventData = {
type: 'login',
ip: 'trusted-ip',
require: true,
};
await extensionService.emit('captcha.validate', eventData);
// Assert
expect(eventData.require).toBe(false);
});
it('should handle multiple extensions modifying captcha requirement', async () => {
// Setup - create two test extensions with different rules
const extension1 = {
name: 'extension-1',
onCaptchaValidate: async (event) => {
if ( event.type === 'login' ) {
event.require = true;
}
},
};
const extension2 = {
name: 'extension-2',
onCaptchaValidate: async (event) => {
if ( event.ip === 'trusted-ip' ) {
event.require = false;
}
},
};
// Register extensions and event handlers
extensionService.registerExtension(extension1.name, extension1);
extensionService.registerExtension(extension2.name, extension2);
extensionService.on('captcha.validate', extension1.onCaptchaValidate);
extensionService.on('captcha.validate', extension2.onCaptchaValidate);
// Test event emission - extension2 should override extension1
const eventData = {
type: 'login',
ip: 'trusted-ip',
require: false,
};
await extensionService.emit('captcha.validate', eventData);
// Assert
expect(eventData.require).toBe(false);
});
// TODO: Why was this behavior changed?
// it('should handle extension errors gracefully', async () => {
// // Setup - create a test extension that throws an error
// const testExtension = {
// name: 'test-extension',
// onCaptchaValidate: async () => {
// throw new Error('Extension error');
// }
// };
// // Register extension and event handler
// extensionService.registerExtension(testExtension.name, testExtension);
// extensionService.on('captcha.validate', testExtension.onCaptchaValidate);
// // Test event emission
// const eventData = {
// type: 'login',
// ip: '1.2.3.4',
// require: false
// };
// // The emit should not throw
// await extensionService.emit('captcha.validate', eventData);
// // Assert - the original value should be preserved
// expect(eventData.require).toBe(false);
// });
});
describe('Backward Compatibility', () => {
it('should maintain backward compatibility with older extension APIs', async () => {
// Setup - create a test extension using the old API format
const legacyExtension = {
name: 'legacy-extension',
handleCaptcha: async (event) => {
event.require = true;
},
};
// Register legacy extension with old event name
extensionService.registerExtension(legacyExtension.name, legacyExtension);
extensionService.on('captcha.check', legacyExtension.handleCaptcha);
// Test both old and new event names
const eventData = {
type: 'login',
ip: '1.2.3.4',
require: false,
};
// Should work with both old and new event names
await extensionService.emit('captcha.check', eventData);
await extensionService.emit('captcha.validate', eventData);
// Assert - the requirement should be set by the legacy extension
expect(eventData.require).toBe(true);
});
it('should support legacy extension configuration formats', async () => {
// Setup - create a test extension with legacy configuration
const legacyExtension = {
name: 'legacy-extension',
config: {
captcha: {
always: true,
types: ['login', 'signup'],
},
},
onCaptchaValidate: async (event) => {
if ( legacyExtension.config.captcha.types.includes(event.type) ) {
event.require = legacyExtension.config.captcha.always;
}
},
};
// Register extension and event handler
extensionService.registerExtension(legacyExtension.name, legacyExtension);
extensionService.on('captcha.validate', legacyExtension.onCaptchaValidate);
// Test event emission
const eventData = {
type: 'login',
ip: '1.2.3.4',
require: false,
};
await extensionService.emit('captcha.validate', eventData);
// Assert
expect(eventData.require).toBe(true);
});
});
});