/** * Integration test for smartregistry with smartstorage * Verifies that smartregistry works with a local S3-compatible server */ import { expect, tap } from '@git.zone/tstest/tapbundle'; import * as smartstorageModule from '@push.rocks/smartstorage'; import { SmartRegistry } from '../ts/classes.smartregistry.js'; import type { IRegistryConfig } from '../ts/core/interfaces.core.js'; import { streamToJson } from '../ts/core/helpers.stream.js'; import * as crypto from 'crypto'; let s3Server: smartstorageModule.SmartStorage; let registry: SmartRegistry; /** * Setup: Start smartstorage server */ tap.test('should start smartstorage server', async () => { s3Server = await smartstorageModule.SmartStorage.createAndStart({ server: { port: 3456, address: '0.0.0.0', silent: true, }, storage: { cleanSlate: true, directory: './.nogit/smartstorage-test-buckets', }, logging: { enabled: false, }, }); expect(s3Server).toBeDefined(); }); /** * Setup: Create SmartRegistry with smartstorage configuration */ tap.test('should create SmartRegistry instance with smarts3 IS3Descriptor', async () => { const s3Descriptor = await s3Server.getStorageDescriptor(); const config: IRegistryConfig = { storage: { ...s3Descriptor, bucketName: 'test-registry-smarts3', }, auth: { jwtSecret: 'test-secret-key', tokenStore: 'memory', npmTokens: { enabled: true, }, ociTokens: { enabled: true, realm: 'https://auth.example.com/token', service: 'test-registry-smarts3', }, pypiTokens: { enabled: true, }, rubygemsTokens: { enabled: true, }, }, npm: { enabled: true, basePath: '/npm', }, oci: { enabled: true, basePath: '/oci', }, pypi: { enabled: true, basePath: '/pypi', }, cargo: { enabled: true, basePath: '/cargo', }, }; registry = new SmartRegistry(config); await registry.init(); expect(registry).toBeDefined(); }); /** * Test NPM protocol with smartstorage */ tap.test('NPM: should publish package to smarts3', async () => { const authManager = registry.getAuthManager(); const userId = await authManager.authenticate({ username: 'testuser', password: 'testpass', }); const token = await authManager.createNpmToken(userId, false); const packageData = { name: 'test-package-smarts3', 'dist-tags': { latest: '1.0.0', }, versions: { '1.0.0': { name: 'test-package-smarts3', version: '1.0.0', description: 'Test package for smarts3 integration', }, }, _attachments: { 'test-package-smarts3-1.0.0.tgz': { content_type: 'application/octet-stream', data: Buffer.from('test tarball content').toString('base64'), length: 20, }, }, }; const response = await registry.handleRequest({ method: 'PUT', path: '/npm/test-package-smarts3', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, query: {}, body: packageData, }); expect(response.status).toEqual(201); }); tap.test('NPM: should retrieve package from smarts3', async () => { const response = await registry.handleRequest({ method: 'GET', path: '/npm/test-package-smarts3', headers: {}, query: {}, }); expect(response.status).toEqual(200); const body = await streamToJson(response.body); expect(body).toHaveProperty('name'); expect(body.name).toEqual('test-package-smarts3'); }); /** * Test OCI protocol with smartstorage */ tap.test('OCI: should store blob in smarts3', async () => { const authManager = registry.getAuthManager(); const userId = await authManager.authenticate({ username: 'testuser', password: 'testpass', }); const token = await authManager.createOciToken( userId, ['oci:repository:test-image:push'], 3600 ); // Initiate blob upload const initiateResponse = await registry.handleRequest({ method: 'POST', path: '/oci/test-image/blobs/uploads/', headers: { 'Authorization': `Bearer ${token}`, }, query: {}, }); expect(initiateResponse.status).toEqual(202); expect(initiateResponse.headers).toHaveProperty('Location'); // Extract upload ID from location const location = initiateResponse.headers['Location']; const uploadId = location.split('/').pop(); // Upload blob data const blobData = Buffer.from('test blob content'); const digest = 'sha256:' + crypto .createHash('sha256') .update(blobData) .digest('hex'); const uploadResponse = await registry.handleRequest({ method: 'PUT', path: `/oci/test-image/blobs/uploads/${uploadId}`, headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/octet-stream', }, query: { digest }, body: blobData, }); expect(uploadResponse.status).toEqual(201); }); /** * Test PyPI protocol with smartstorage */ tap.test('PyPI: should upload package to smarts3', async () => { const storage = registry.getStorage(); // Store a test package file const packageContent = Buffer.from('test wheel content'); await storage.putPypiPackageFile( 'test-package', 'test_package-1.0.0-py3-none-any.whl', packageContent ); // Store metadata const metadata = { name: 'test-package', version: '1.0.0', files: [ { filename: 'test_package-1.0.0-py3-none-any.whl', url: '/packages/test-package/test_package-1.0.0-py3-none-any.whl', hashes: { sha256: 'abc123' }, }, ], }; await storage.putPypiPackageMetadata('test-package', metadata); // Verify stored const retrievedMetadata = await storage.getPypiPackageMetadata('test-package'); expect(retrievedMetadata).toBeDefined(); expect(retrievedMetadata.name).toEqual('test-package'); }); /** * Test Cargo protocol with smartstorage */ tap.test('Cargo: should store crate in smarts3', async () => { const storage = registry.getStorage(); // Store a test crate index entry const indexEntry = { name: 'test-crate', vers: '1.0.0', deps: [], cksum: 'abc123', features: {}, yanked: false, }; await storage.putCargoIndex('test-crate', [indexEntry]); // Store the actual .crate file const crateContent = Buffer.from('test crate tarball'); await storage.putCargoCrate('test-crate', '1.0.0', crateContent); // Verify stored const retrievedIndex = await storage.getCargoIndex('test-crate'); expect(retrievedIndex).toBeDefined(); expect(retrievedIndex.length).toEqual(1); expect(retrievedIndex[0].name).toEqual('test-crate'); }); /** * Cleanup: Stop smartstorage server */ tap.test('should stop smartstorage server', async () => { await s3Server.stop(); expect(true).toEqual(true); }); export default tap.start();