import { expect, tap } from '@git.zone/tstest/tapbundle'; import { SmartRegistry } from '../ts/index.js'; import { createTestRegistryWithUpstream, createTrackingUpstreamProvider, } from './helpers/registry.js'; import { StaticUpstreamProvider } from '../ts/upstream/interfaces.upstream.js'; import type { IUpstreamProvider, IUpstreamResolutionContext, IProtocolUpstreamConfig, } from '../ts/upstream/interfaces.upstream.js'; import type { TRegistryProtocol } from '../ts/core/interfaces.core.js'; // ============================================================================= // StaticUpstreamProvider Tests // ============================================================================= tap.test('StaticUpstreamProvider: should return config for configured protocol', async () => { const npmConfig: IProtocolUpstreamConfig = { enabled: true, upstreams: [{ id: 'npmjs', url: 'https://registry.npmjs.org', priority: 1, enabled: true }], }; const provider = new StaticUpstreamProvider({ npm: npmConfig, }); const result = await provider.resolveUpstreamConfig({ protocol: 'npm', resource: 'lodash', scope: null, method: 'GET', resourceType: 'packument', }); expect(result).toBeDefined(); expect(result?.enabled).toEqual(true); expect(result?.upstreams[0].id).toEqual('npmjs'); }); tap.test('StaticUpstreamProvider: should return null for unconfigured protocol', async () => { const provider = new StaticUpstreamProvider({ npm: { enabled: true, upstreams: [{ id: 'npmjs', url: 'https://registry.npmjs.org', priority: 1, enabled: true }], }, }); const result = await provider.resolveUpstreamConfig({ protocol: 'maven', resource: 'com.example:lib', scope: 'com.example', method: 'GET', resourceType: 'pom', }); expect(result).toBeNull(); }); tap.test('StaticUpstreamProvider: should support multiple protocols', async () => { const provider = new StaticUpstreamProvider({ npm: { enabled: true, upstreams: [{ id: 'npmjs', url: 'https://registry.npmjs.org', priority: 1, enabled: true }], }, oci: { enabled: true, upstreams: [{ id: 'dockerhub', url: 'https://registry-1.docker.io', priority: 1, enabled: true }], }, maven: { enabled: true, upstreams: [{ id: 'central', url: 'https://repo1.maven.org/maven2', priority: 1, enabled: true }], }, }); const npmResult = await provider.resolveUpstreamConfig({ protocol: 'npm', resource: 'lodash', scope: null, method: 'GET', resourceType: 'packument', }); expect(npmResult?.upstreams[0].id).toEqual('npmjs'); const ociResult = await provider.resolveUpstreamConfig({ protocol: 'oci', resource: 'library/nginx', scope: 'library', method: 'GET', resourceType: 'manifest', }); expect(ociResult?.upstreams[0].id).toEqual('dockerhub'); const mavenResult = await provider.resolveUpstreamConfig({ protocol: 'maven', resource: 'com.example:lib', scope: 'com.example', method: 'GET', resourceType: 'pom', }); expect(mavenResult?.upstreams[0].id).toEqual('central'); }); // ============================================================================= // Registry with Provider Integration Tests // ============================================================================= let registry: SmartRegistry; let trackingProvider: ReturnType; tap.test('Provider Integration: should create registry with upstream provider', async () => { trackingProvider = createTrackingUpstreamProvider({ npm: { enabled: true, upstreams: [{ id: 'test-npm', url: 'https://registry.npmjs.org', priority: 1, enabled: true }], }, }); registry = await createTestRegistryWithUpstream(trackingProvider.provider); expect(registry).toBeInstanceOf(SmartRegistry); expect(registry.isInitialized()).toEqual(true); }); tap.test('Provider Integration: should call provider when fetching unknown npm package', async () => { // Clear previous calls trackingProvider.calls.length = 0; // Request a package that doesn't exist locally - should trigger upstream lookup const response = await registry.handleRequest({ method: 'GET', path: '/npm/@test-scope/nonexistent-package', headers: {}, query: {}, }); // Provider should have been called for the packument lookup const npmCalls = trackingProvider.calls.filter(c => c.protocol === 'npm'); // The package doesn't exist locally, so upstream should be consulted // Note: actual upstream fetch may fail since the package doesn't exist expect(response.status).toBeOneOf([404, 200, 502]); // 404 if not found, 502 if upstream error }); tap.test('Provider Integration: provider receives correct context for scoped npm package', async () => { trackingProvider.calls.length = 0; // Use URL-encoded path for scoped packages as npm client does await registry.handleRequest({ method: 'GET', path: '/npm/@myorg%2fmy-package', headers: {}, query: {}, }); // Find any npm call - the exact resource type depends on routing const npmCalls = trackingProvider.calls.filter(c => c.protocol === 'npm'); // Provider should be called for upstream lookup if (npmCalls.length > 0) { const call = npmCalls[0]; expect(call.protocol).toEqual('npm'); // The resource should include the scoped name expect(call.resource).toInclude('myorg'); expect(call.method).toEqual('GET'); } }); tap.test('Provider Integration: provider receives correct context for unscoped npm package', async () => { trackingProvider.calls.length = 0; await registry.handleRequest({ method: 'GET', path: '/npm/lodash', headers: {}, query: {}, }); const packumentCall = trackingProvider.calls.find( c => c.protocol === 'npm' && c.resourceType === 'packument' ); if (packumentCall) { expect(packumentCall.protocol).toEqual('npm'); expect(packumentCall.resource).toEqual('lodash'); expect(packumentCall.scope).toBeNull(); // No scope for unscoped package } }); // ============================================================================= // Custom Provider Implementation Tests // ============================================================================= tap.test('Custom Provider: should support dynamic resolution based on context', async () => { // Create a provider that returns different configs based on scope const dynamicProvider: IUpstreamProvider = { async resolveUpstreamConfig(context: IUpstreamResolutionContext) { if (context.scope === 'internal') { // Internal packages go to private registry return { enabled: true, upstreams: [{ id: 'private', url: 'https://private.registry.com', priority: 1, enabled: true }], }; } // Everything else goes to public registry return { enabled: true, upstreams: [{ id: 'public', url: 'https://registry.npmjs.org', priority: 1, enabled: true }], }; }, }; const internalResult = await dynamicProvider.resolveUpstreamConfig({ protocol: 'npm', resource: '@internal/utils', scope: 'internal', method: 'GET', resourceType: 'packument', }); expect(internalResult?.upstreams[0].id).toEqual('private'); const publicResult = await dynamicProvider.resolveUpstreamConfig({ protocol: 'npm', resource: '@public/utils', scope: 'public', method: 'GET', resourceType: 'packument', }); expect(publicResult?.upstreams[0].id).toEqual('public'); }); tap.test('Custom Provider: should support actor-based resolution', async () => { const actorAwareProvider: IUpstreamProvider = { async resolveUpstreamConfig(context: IUpstreamResolutionContext) { // Different upstreams based on user's organization if (context.actor?.orgId === 'enterprise-org') { return { enabled: true, upstreams: [{ id: 'enterprise', url: 'https://enterprise.registry.com', priority: 1, enabled: true }], }; } return { enabled: true, upstreams: [{ id: 'default', url: 'https://registry.npmjs.org', priority: 1, enabled: true }], }; }, }; const enterpriseResult = await actorAwareProvider.resolveUpstreamConfig({ protocol: 'npm', resource: 'lodash', scope: null, actor: { orgId: 'enterprise-org', userId: 'user1' }, method: 'GET', resourceType: 'packument', }); expect(enterpriseResult?.upstreams[0].id).toEqual('enterprise'); const defaultResult = await actorAwareProvider.resolveUpstreamConfig({ protocol: 'npm', resource: 'lodash', scope: null, actor: { orgId: 'free-org', userId: 'user2' }, method: 'GET', resourceType: 'packument', }); expect(defaultResult?.upstreams[0].id).toEqual('default'); }); tap.test('Custom Provider: should support disabling upstream for specific resources', async () => { const selectiveProvider: IUpstreamProvider = { async resolveUpstreamConfig(context: IUpstreamResolutionContext) { // Block upstream for internal packages if (context.scope === 'internal') { return null; // No upstream for internal packages } return { enabled: true, upstreams: [{ id: 'public', url: 'https://registry.npmjs.org', priority: 1, enabled: true }], }; }, }; const internalResult = await selectiveProvider.resolveUpstreamConfig({ protocol: 'npm', resource: '@internal/secret', scope: 'internal', method: 'GET', resourceType: 'packument', }); expect(internalResult).toBeNull(); const publicResult = await selectiveProvider.resolveUpstreamConfig({ protocol: 'npm', resource: 'lodash', scope: null, method: 'GET', resourceType: 'packument', }); expect(publicResult).not.toBeNull(); }); // ============================================================================= // Registry without Provider Tests // ============================================================================= tap.test('No Provider: registry should work without upstream provider', async () => { const registryWithoutUpstream = await createTestRegistryWithUpstream( // Pass a provider that always returns null { async resolveUpstreamConfig() { return null; }, } ); expect(registryWithoutUpstream).toBeInstanceOf(SmartRegistry); // Should return 404 for non-existent package (no upstream to check) const response = await registryWithoutUpstream.handleRequest({ method: 'GET', path: '/npm/nonexistent-package-xyz', headers: {}, query: {}, }); expect(response.status).toEqual(404); registryWithoutUpstream.destroy(); }); // ============================================================================= // Cleanup // ============================================================================= tap.postTask('cleanup registry', async () => { if (registry) { registry.destroy(); } }); export default tap.start();