import { tap, expect } from '@git.zone/tstest/tapbundle'; import * as helpers from './helpers/index.js'; import * as cloudly from '../ts/index.js'; import * as cloudlyApiClient from '@serve.zone/api'; let testCloudly: cloudly.Cloudly; let testClient: cloudlyApiClient.CloudlyApiClient; tap.preTask('should start cloudly', async () => { testCloudly = await helpers.createCloudly(); await testCloudly.start(); }); tap.preTask('should create a new machine user for testing', async () => { console.log('🔵 PreTask: Creating first machine user...'); const machineUser = new testCloudly.authManager.CUser(); machineUser.id = await testCloudly.authManager.CUser.getNewId(); console.log(` - User ID: ${machineUser.id}`); machineUser.data = { type: 'machine', username: 'test', password: 'test', tokens: [{ token: 'test', expiresAt: Date.now() + 3600 * 1000 * 24 * 365, assignedRoles: ['admin'], }], role: 'admin', }; console.log(` - Username: ${machineUser.data.username}`); console.log(` - Role: ${machineUser.data.role}`); console.log(` - Token: 'test'`); console.log(` - Token roles: ${machineUser.data.tokens[0].assignedRoles}`); await machineUser.save(); console.log('✅ PreTask: First machine user saved successfully'); }); tap.test('should create a new cloudlyApiClient', async () => { console.log('🔵 Test: Creating CloudlyApiClient...'); testClient = new cloudlyApiClient.CloudlyApiClient({ registerAs: 'api', cloudlyUrl: `http://${helpers.testCloudlyConfig.publicUrl}:${helpers.testCloudlyConfig.publicPort}`, }); console.log(` - URL: http://${helpers.testCloudlyConfig.publicUrl}:${helpers.testCloudlyConfig.publicPort}`); await testClient.start(); console.log('✅ CloudlyApiClient started successfully'); expect(testClient).toBeTruthy(); }); tap.test('DEBUG: Check existing users', async () => { console.log('🔍 DEBUG: Checking existing users in database...'); const allUsers = await testCloudly.authManager.CUser.getInstances({}); console.log(` - Total users found: ${allUsers.length}`); for (const user of allUsers) { console.log(` - User: ${user.data.username} (ID: ${user.id})`); console.log(` - Type: ${user.data.type}`); console.log(` - Role: ${user.data.role}`); console.log(` - Tokens: ${user.data.tokens?.length ?? 0}`); for (const token of user.data.tokens ?? []) { console.log(` - Token: '${token.token}' | Roles: ${token.assignedRoles?.join(', ')}`); } } }); tap.test('should get an identity', async () => { console.log('🔵 Test: Getting identity by token...'); console.log(` - Using token: 'test'`); console.log(` - API URL: http://${helpers.testCloudlyConfig.publicUrl}:${helpers.testCloudlyConfig.publicPort}`); try { const identity = await testClient.getIdentityByToken('test'); console.log('✅ Identity retrieved successfully:'); console.log(` - Identity exists: ${!!identity}`); if (identity) { console.log(` - Identity data:`, JSON.stringify(identity, null, 2)); } expect(identity).toBeTruthy(); } catch (error) { console.error('❌ Failed to get identity:'); console.error(` - Error message: ${error.message}`); console.error(` - Error stack:`, error.stack); throw error; } }); tap.test('should expose the OCI registry endpoint', async () => { const response = await fetch( `http://${helpers.testCloudlyConfig.publicUrl}:${helpers.testCloudlyConfig.publicPort}/v2/`, ); expect(response.status).toEqual(200); expect(response.headers.get('docker-distribution-api-version')).toEqual('registry/2.0'); }); tap.test('should require authentication for OCI registry tokens', async () => { const response = await fetch( `http://${helpers.testCloudlyConfig.publicUrl}:${helpers.testCloudlyConfig.publicPort}/v2/token?service=cloudly&scope=repository:test/app:pull`, ); expect(response.status).toEqual(401); }); tap.test('should issue OCI registry tokens for the initial admin', async () => { const credentials = Buffer.from( `${helpers.testCloudlyAdminAccount.username}:${helpers.testCloudlyAdminAccount.password}`, ).toString('base64'); const response = await fetch( `http://${helpers.testCloudlyConfig.publicUrl}:${helpers.testCloudlyConfig.publicPort}/v2/token?service=cloudly&scope=repository:test/app:pull,push`, { headers: { Authorization: `Basic ${credentials}`, }, }, ); const body = await response.json(); expect(response.status).toEqual(200); expect(body.token).toBeTruthy(); expect(body.access_token).toEqual(body.token); }); tap.test('should deny OCI registry push tokens for non-admin users', async () => { const readonlyUsername = 'registry-readonly'; const readonlyToken = 'registry-readonly-token'; const readonlyUser = new testCloudly.authManager.CUser(); readonlyUser.id = await testCloudly.authManager.CUser.getNewId(); readonlyUser.data = { type: 'machine', username: readonlyUsername, password: readonlyToken, tokens: [{ token: readonlyToken, expiresAt: Date.now() + 3600 * 1000, assignedRoles: [], }], role: 'user', }; await readonlyUser.save(); const credentials = Buffer.from(`${readonlyUsername}:${readonlyToken}`).toString('base64'); const pullResponse = await fetch( `http://${helpers.testCloudlyConfig.publicUrl}:${helpers.testCloudlyConfig.publicPort}/v2/token?service=cloudly&scope=repository:test/readonly:pull`, { headers: { Authorization: `Basic ${credentials}`, }, }, ); expect(pullResponse.status).toEqual(200); const pushResponse = await fetch( `http://${helpers.testCloudlyConfig.publicUrl}:${helpers.testCloudlyConfig.publicPort}/v2/token?service=cloudly&scope=repository:test/readonly:pull,push`, { headers: { Authorization: `Basic ${credentials}`, }, }, ); expect(pushResponse.status).toEqual(403); }); tap.test('should expose platform desired state', async () => { const capabilitiesResponse = await testClient.platform.getPlatformCapabilities(); expect(capabilitiesResponse.capabilities.find((capability) => capability.id === 'database')).toBeTruthy(); const desiredState = await testClient.platform.getPlatformDesiredState(); expect(desiredState.capabilities).toBeTruthy(); expect(desiredState.providerConfigs).toBeTruthy(); expect(desiredState.bindings).toBeTruthy(); }); let platformProviderConfigId: string; let platformBindingId: string; tap.test('should upsert platform provider config and binding', async () => { const providerConfigResponse = await testClient.platform.upsertPlatformProviderConfig({ id: '', capability: 'database', providerType: 'docker', name: 'Local Docker Database', enabled: true, }); platformProviderConfigId = providerConfigResponse.providerConfig.id; expect(platformProviderConfigId).toBeTruthy(); const bindingResponse = await testClient.platform.upsertPlatformBinding({ id: '', serviceId: 'test-service', capability: 'database', desiredState: 'enabled', status: 'requested', providerConfigId: platformProviderConfigId, }); platformBindingId = bindingResponse.binding.id; expect(platformBindingId).toBeTruthy(); const statusResponse = await testClient.platform.updatePlatformBindingStatus({ bindingId: platformBindingId, status: 'ready', endpoints: [ { name: 'primary', capability: 'database', protocol: 'mongodb', internalUrl: 'mongodb://platform-database:27017/test-service', }, ], }); expect(statusResponse.binding.status).toEqual('ready'); const bindingsResponse = await testClient.platform.getPlatformBindings({ serviceId: 'test-service', }); expect(bindingsResponse.bindings.find((binding) => binding.id === platformBindingId)).toBeTruthy(); }); let image: any; tap.test('should create and upload an image', async () => { console.log('🔵 Test: Creating and uploading image...'); console.log(` - Image name: 'test'`); console.log(` - Image description: 'test'`); try { image = await testClient.image.createImage({ name: 'test', description: 'test' }); console.log('✅ Image created successfully:'); console.log(` - Image ID: ${image?.id}`); console.log(` - Image data:`, image); expect(image).toBeTruthy(); } catch (error) { console.error('❌ Failed to create image:'); console.error(` - Error message: ${error.message}`); console.error(` - Error stack:`, error.stack); throw error; } }) tap.test('should upload an image version', async () => { console.log('🔵 Test: Uploading image version...'); console.log(` - Version: 'v1.0.0'`); console.log(` - Image exists: ${!!image}`); console.log(` - Image ID: ${image?.id}`); try { const imageStream = await helpers.getAlpineImageReadableStream(); console.log(' - Image stream obtained successfully'); await image.pushImageVersion('v1.0.0', imageStream); console.log('✅ Image version uploaded successfully'); } catch (error) { console.error('❌ Failed to upload image version:'); console.error(` - Error message: ${error.message}`); console.error(` - Error stack:`, error.stack); throw error; } }); tap.test('should stop the apiclient', async (toolsArg) => { await toolsArg.delayFor(10000); await helpers.stopCloudly(); await testClient.stop(); await testCloudly.stop(); }) export default tap.start();