/** * Integration test for smartregistry with smarts3 * Verifies that smartregistry works with a local S3-compatible server */ import { expect, tap } from '@git.zone/tstest/tapbundle'; import * as smarts3Module from '@push.rocks/smarts3'; import { SmartRegistry } from '../ts/classes.smartregistry.js'; import type { IRegistryConfig } from '../ts/core/interfaces.core.js'; import * as crypto from 'crypto'; let s3Server: smarts3Module.Smarts3; let registry: SmartRegistry; /** * Setup: Start smarts3 server */ tap.test('should start smarts3 server', async () => { s3Server = await smarts3Module.Smarts3.createAndStart({ server: { port: 3456, // Use different port to avoid conflicts with other tests host: '0.0.0.0', }, storage: { cleanSlate: true, // Fresh storage for each test run bucketsDir: './.nogit/smarts3-test-buckets', }, logging: { silent: true, // Reduce test output noise }, }); expect(s3Server).toBeDefined(); }); /** * Setup: Create SmartRegistry with smarts3 configuration */ tap.test('should create SmartRegistry instance with smarts3 IS3Descriptor', async () => { // Manually construct IS3Descriptor based on smarts3 configuration // Note: smarts3.getS3Descriptor() returns empty object as of v5.1.0 // This is a known limitation - smarts3 doesn't expose its config properly const s3Descriptor = { endpoint: 'localhost', port: 3456, accessKey: 'test', // smarts3 doesn't require real credentials accessSecret: 'test', useSsl: false, region: 'us-east-1', }; 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 smarts3 */ 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); // 201 Created is correct for publishing }); 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); expect(response.body).toHaveProperty('name'); expect(response.body.name).toEqual('test-package-smarts3'); }); /** * Test OCI protocol with smarts3 */ 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/v2/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/v2/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 smarts3 */ tap.test('PyPI: should upload package to smarts3', async () => { const authManager = registry.getAuthManager(); const userId = await authManager.authenticate({ username: 'testuser', password: 'testpass', }); const token = await authManager.createPypiToken(userId, false); // Note: In a real test, this would be multipart/form-data // For simplicity, we're testing the storage layer 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 smarts3 */ 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 smarts3 server */ tap.test('should stop smarts3 server', async () => { await s3Server.stop(); expect(true).toEqual(true); // Just verify it completes without error }); export default tap.start();