-
Notifications
You must be signed in to change notification settings - Fork 0
[WIP] Verify compatibility of plugin-auth with API discovery #584
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2dc4d7e
493b878
015df1e
4c271d0
68b01f7
3f83f4a
4e57c9d
106d98f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. | ||
|
|
||
| /** | ||
| * Discovery Test | ||
| * | ||
| * This test verifies that the discovery endpoint correctly reflects | ||
| * the availability of the auth service when plugin-auth is registered. | ||
| */ | ||
|
|
||
| import { ObjectKernel } from '@objectstack/core'; | ||
| import { ObjectQL } from '@objectstack/objectql'; | ||
| import { ObjectQLPlugin } from '@objectstack/objectql'; | ||
| import { InMemoryDriver } from '@objectstack/driver-memory'; | ||
| import { AuthPlugin } from '@objectstack/plugin-auth'; | ||
|
|
||
| async function testDiscovery() { | ||
| console.log('🧪 Testing Discovery with Auth Plugin...\n'); | ||
|
|
||
| // 1. Create ObjectQL instance with in-memory driver | ||
| const objectql = new ObjectQL(); | ||
| await objectql.registerDriver(new InMemoryDriver()); | ||
|
|
||
| // 2. Create kernel | ||
| const kernel = new ObjectKernel(); | ||
|
|
||
| // 3. Register ObjectQL plugin (which provides protocol service) | ||
| await kernel.use(new ObjectQLPlugin(objectql)); | ||
|
|
||
| // 4. Get discovery BEFORE auth is registered | ||
| console.log('📋 Discovery BEFORE auth plugin:'); | ||
| let protocol = kernel.getService('protocol'); | ||
| let discovery = await protocol.getDiscovery(); | ||
| console.log(' - Auth service enabled:', discovery.services?.auth?.enabled); | ||
| console.log(' - Auth service status:', discovery.services?.auth?.status); | ||
| console.log(' - Auth endpoint:', discovery.endpoints?.auth || 'undefined'); | ||
| console.log(''); | ||
|
|
||
| // 5. Register auth plugin | ||
| await kernel.use(new AuthPlugin({ | ||
| secret: 'test-secret-min-32-characters-long-for-jwt-signing', | ||
| baseUrl: 'http://localhost:3000', | ||
| })); | ||
|
Comment on lines
+23
to
+42
|
||
|
|
||
| // 6. Get discovery AFTER auth is registered | ||
| console.log('📋 Discovery AFTER auth plugin:'); | ||
| protocol = kernel.getService('protocol'); | ||
| discovery = await protocol.getDiscovery(); | ||
| console.log(' - Auth service enabled:', discovery.services?.auth?.enabled); | ||
| console.log(' - Auth service status:', discovery.services?.auth?.status); | ||
| console.log(' - Auth service route:', discovery.services?.auth?.route); | ||
| console.log(' - Auth service provider:', discovery.services?.auth?.provider); | ||
| console.log(' - Auth endpoint:', discovery.endpoints?.auth); | ||
| console.log(''); | ||
|
|
||
| // 7. Verify the results | ||
| if (discovery.services?.auth?.enabled && discovery.services?.auth?.status === 'available') { | ||
| console.log('✅ SUCCESS: Auth service is correctly shown as available!'); | ||
| console.log('✅ Discovery endpoint is working correctly!'); | ||
| } else { | ||
| console.log('❌ FAILED: Auth service should be available but is not!'); | ||
| console.log(' Actual status:', discovery.services?.auth); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| // 8. Clean up | ||
| await kernel.shutdown(); | ||
| console.log('\n🎉 All tests passed!'); | ||
| } | ||
|
|
||
| testDiscovery().catch((error) => { | ||
| console.error('❌ Error:', error); | ||
| process.exit(1); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. | ||
|
|
||
| import { describe, it, expect, beforeEach } from 'vitest'; | ||
| import { ObjectStackProtocolImplementation } from './protocol.js'; | ||
| import { ObjectQL } from './engine.js'; | ||
|
|
||
| describe('ObjectStackProtocolImplementation - Dynamic Service Discovery', () => { | ||
| let protocol: ObjectStackProtocolImplementation; | ||
| let engine: ObjectQL; | ||
|
|
||
| beforeEach(() => { | ||
| engine = new ObjectQL(); | ||
| }); | ||
|
|
||
| it('should return unavailable auth service when no services registered', async () => { | ||
| // Create protocol without service registry | ||
| protocol = new ObjectStackProtocolImplementation(engine); | ||
|
|
||
| const discovery = await protocol.getDiscovery(); | ||
|
|
||
| expect(discovery.services.auth).toBeDefined(); | ||
| expect(discovery.services.auth.enabled).toBe(false); | ||
| expect(discovery.services.auth.status).toBe('unavailable'); | ||
| expect(discovery.services.auth.message).toContain('plugin-auth'); | ||
| expect(discovery.capabilities.workflow).toBe(false); | ||
| }); | ||
|
|
||
| it('should return available auth service when auth is registered', async () => { | ||
| // Mock service registry with auth service | ||
| const mockServices = new Map<string, any>(); | ||
| mockServices.set('auth', { /* mock auth service */ }); | ||
|
|
||
| protocol = new ObjectStackProtocolImplementation(engine, () => mockServices); | ||
|
|
||
| const discovery = await protocol.getDiscovery(); | ||
|
|
||
| expect(discovery.services.auth).toBeDefined(); | ||
| expect(discovery.services.auth.enabled).toBe(true); | ||
| expect(discovery.services.auth.status).toBe('available'); | ||
| expect(discovery.services.auth.route).toBe('/api/v1/auth'); | ||
| expect(discovery.services.auth.provider).toBe('plugin-auth'); | ||
| expect(discovery.endpoints.auth).toBe('/api/v1/auth'); | ||
| }); | ||
|
|
||
| it('should return available workflow when automation service is registered', async () => { | ||
| const mockServices = new Map<string, any>(); | ||
| mockServices.set('automation', { /* mock automation service */ }); | ||
|
|
||
| protocol = new ObjectStackProtocolImplementation(engine, () => mockServices); | ||
|
|
||
| const discovery = await protocol.getDiscovery(); | ||
|
|
||
| expect(discovery.services.automation).toBeDefined(); | ||
| expect(discovery.services.automation.enabled).toBe(true); | ||
| expect(discovery.services.automation.status).toBe('available'); | ||
| expect(discovery.capabilities.workflow).toBe(true); | ||
| }); | ||
|
|
||
| it('should return multiple available services when registered', async () => { | ||
| const mockServices = new Map<string, any>(); | ||
| mockServices.set('auth', {}); | ||
| mockServices.set('realtime', {}); | ||
| mockServices.set('ai', {}); | ||
|
|
||
| protocol = new ObjectStackProtocolImplementation(engine, () => mockServices); | ||
|
|
||
| const discovery = await protocol.getDiscovery(); | ||
|
|
||
| // Check auth | ||
| expect(discovery.services.auth.enabled).toBe(true); | ||
| expect(discovery.services.auth.status).toBe('available'); | ||
|
|
||
| // Check realtime | ||
| expect(discovery.services.realtime.enabled).toBe(true); | ||
| expect(discovery.services.realtime.status).toBe('available'); | ||
| expect(discovery.capabilities.websockets).toBe(true); | ||
|
|
||
| // Check AI | ||
| expect(discovery.services.ai.enabled).toBe(true); | ||
| expect(discovery.services.ai.status).toBe('available'); | ||
| expect(discovery.capabilities.ai).toBe(true); | ||
|
|
||
| // Endpoints should include available services | ||
| expect(discovery.endpoints.auth).toBe('/api/v1/auth'); | ||
| expect(discovery.endpoints.realtime).toBe('/api/v1/realtime'); | ||
| expect(discovery.endpoints.ai).toBe('/api/v1/ai'); | ||
| }); | ||
|
|
||
| it('should always show core services as available', async () => { | ||
| protocol = new ObjectStackProtocolImplementation(engine); | ||
|
|
||
| const discovery = await protocol.getDiscovery(); | ||
|
|
||
| // Core services should always be available | ||
| expect(discovery.services.metadata.enabled).toBe(true); | ||
| expect(discovery.services.metadata.status).toBe('degraded'); | ||
| expect(discovery.services.data.enabled).toBe(true); | ||
| expect(discovery.services.data.status).toBe('available'); | ||
| expect(discovery.services.analytics.enabled).toBe(true); | ||
| expect(discovery.services.analytics.status).toBe('available'); | ||
|
|
||
| // Core capabilities | ||
| expect(discovery.capabilities.analytics).toBe(true); | ||
| }); | ||
|
|
||
| it('should map file-storage service to storage endpoint', async () => { | ||
| const mockServices = new Map<string, any>(); | ||
| mockServices.set('file-storage', {}); | ||
|
|
||
| protocol = new ObjectStackProtocolImplementation(engine, () => mockServices); | ||
|
|
||
| const discovery = await protocol.getDiscovery(); | ||
|
|
||
| expect(discovery.services['file-storage'].enabled).toBe(true); | ||
| expect(discovery.services['file-storage'].status).toBe('available'); | ||
| expect(discovery.endpoints.storage).toBe('/api/v1/storage'); | ||
| expect(discovery.capabilities.files).toBe(true); | ||
| }); | ||
|
Comment on lines
+106
to
+118
|
||
|
|
||
| it('should handle workflow capability from either automation or workflow service', async () => { | ||
| // Test with workflow service | ||
| const mockServicesWithWorkflow = new Map<string, any>(); | ||
| mockServicesWithWorkflow.set('workflow', {}); | ||
|
|
||
| protocol = new ObjectStackProtocolImplementation(engine, () => mockServicesWithWorkflow); | ||
| let discovery = await protocol.getDiscovery(); | ||
| expect(discovery.capabilities.workflow).toBe(true); | ||
|
|
||
| // Test with automation service | ||
| const mockServicesWithAutomation = new Map<string, any>(); | ||
| mockServicesWithAutomation.set('automation', {}); | ||
|
|
||
| protocol = new ObjectStackProtocolImplementation(engine, () => mockServicesWithAutomation); | ||
| discovery = await protocol.getDiscovery(); | ||
| expect(discovery.capabilities.workflow).toBe(true); | ||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The README claims
src/test-discovery.tsshows discovery “before the plugin is registered” and then “after registration”, but withObjectKernelyou can’t add plugins afterbootstrap(), and withoutbootstrap()theprotocolservice won’t exist. Once the script is adjusted (e.g. two kernels / LiteKernel), this section should be updated to reflect the actual steps needed to run the discovery demo successfully.