204 lines
5.9 KiB
TypeScript
204 lines
5.9 KiB
TypeScript
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
|
import { SmartRegistry } from '../ts/index.js';
|
|
import { createTestRegistry, createTestTokens } from './helpers/registry.js';
|
|
|
|
let registry: SmartRegistry;
|
|
|
|
tap.test('Integration: should create SmartRegistry instance with both protocols', async () => {
|
|
registry = await createTestRegistry();
|
|
expect(registry).toBeInstanceOf(SmartRegistry);
|
|
expect(registry.isInitialized()).toEqual(true);
|
|
});
|
|
|
|
tap.test('Integration: should have both OCI and NPM registries enabled', async () => {
|
|
const ociRegistry = registry.getRegistry('oci');
|
|
const npmRegistry = registry.getRegistry('npm');
|
|
|
|
expect(ociRegistry).toBeDefined();
|
|
expect(npmRegistry).toBeDefined();
|
|
expect(ociRegistry?.getBasePath()).toEqual('/oci');
|
|
expect(npmRegistry?.getBasePath()).toEqual('/npm');
|
|
});
|
|
|
|
tap.test('Integration: should route OCI requests correctly', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: '/oci/v2/',
|
|
headers: {},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
expect(response.headers['Docker-Distribution-API-Version']).toEqual('registry/2.0');
|
|
});
|
|
|
|
tap.test('Integration: should route NPM requests correctly', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: '/npm/some-package',
|
|
headers: {},
|
|
query: {},
|
|
});
|
|
|
|
// Will return 404 since package doesn't exist, but should route correctly
|
|
expect(response.status).toEqual(404);
|
|
expect(response.headers['Content-Type']).toEqual('application/json');
|
|
});
|
|
|
|
tap.test('Integration: should return 404 for unknown paths', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: '/unknown/path',
|
|
headers: {},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(404);
|
|
expect(response.body).toHaveProperty('error');
|
|
expect((response.body as any).error).toEqual('NOT_FOUND');
|
|
});
|
|
|
|
tap.test('Integration: should create and validate tokens', async () => {
|
|
const tokens = await createTestTokens(registry);
|
|
|
|
expect(tokens.npmToken).toBeTypeOf('string');
|
|
expect(tokens.ociToken).toBeTypeOf('string');
|
|
expect(tokens.userId).toBeTypeOf('string');
|
|
|
|
// Validate NPM token
|
|
const authManager = registry.getAuthManager();
|
|
const npmTokenObj = await authManager.validateToken(tokens.npmToken, 'npm');
|
|
expect(npmTokenObj).toBeDefined();
|
|
expect(npmTokenObj?.type).toEqual('npm');
|
|
expect(npmTokenObj?.userId).toEqual(tokens.userId);
|
|
|
|
// Validate OCI token
|
|
const ociTokenObj = await authManager.validateToken(tokens.ociToken, 'oci');
|
|
expect(ociTokenObj).toBeDefined();
|
|
expect(ociTokenObj?.type).toEqual('oci');
|
|
expect(ociTokenObj?.userId).toEqual(tokens.userId);
|
|
});
|
|
|
|
tap.test('Integration: should handle authentication properly', async () => {
|
|
const authManager = registry.getAuthManager();
|
|
|
|
// Create a new user
|
|
const userId = await authManager.authenticate({
|
|
username: 'newuser',
|
|
password: 'newpass',
|
|
});
|
|
|
|
expect(userId).toBeTypeOf('string');
|
|
expect(userId).toEqual('newuser');
|
|
|
|
// Verify login with correct credentials
|
|
const userId2 = await authManager.authenticate({
|
|
username: 'newuser',
|
|
password: 'newpass',
|
|
});
|
|
|
|
expect(userId2).toEqual('newuser');
|
|
|
|
// Verify login fails with wrong credentials
|
|
const userId3 = await authManager.authenticate({
|
|
username: 'newuser',
|
|
password: 'wrongpass',
|
|
});
|
|
|
|
expect(userId3).toBeNull();
|
|
});
|
|
|
|
tap.test('Integration: should handle scoped permissions correctly', async () => {
|
|
const authManager = registry.getAuthManager();
|
|
|
|
// Create user and token with specific scopes
|
|
const userId = await authManager.authenticate({
|
|
username: 'scopeduser',
|
|
password: 'pass',
|
|
});
|
|
|
|
const npmToken = await authManager.createNpmToken(userId!, false);
|
|
const tokenObj = await authManager.validateToken(npmToken, 'npm');
|
|
|
|
// Check authorization for different resources
|
|
const canWrite = await authManager.authorize(
|
|
tokenObj,
|
|
'npm:package:test-package',
|
|
'write'
|
|
);
|
|
expect(canWrite).toEqual(true);
|
|
|
|
const canRead = await authManager.authorize(
|
|
tokenObj,
|
|
'npm:package:test-package',
|
|
'read'
|
|
);
|
|
expect(canRead).toEqual(true);
|
|
});
|
|
|
|
tap.test('Integration: should respect readonly token restrictions', async () => {
|
|
const authManager = registry.getAuthManager();
|
|
|
|
const userId = await authManager.authenticate({
|
|
username: 'readonlyuser',
|
|
password: 'pass',
|
|
});
|
|
|
|
const readonlyToken = await authManager.createNpmToken(userId!, true);
|
|
const tokenObj = await authManager.validateToken(readonlyToken, 'npm');
|
|
|
|
// Readonly token should allow read
|
|
const canRead = await authManager.authorize(
|
|
tokenObj,
|
|
'npm:package:test-package',
|
|
'read'
|
|
);
|
|
expect(canRead).toEqual(true);
|
|
|
|
// Readonly token should deny write
|
|
const canWrite = await authManager.authorize(
|
|
tokenObj,
|
|
'npm:package:test-package',
|
|
'write'
|
|
);
|
|
expect(canWrite).toEqual(false);
|
|
|
|
// Readonly token should deny push
|
|
const canPush = await authManager.authorize(
|
|
tokenObj,
|
|
'oci:repository:test-repo',
|
|
'push'
|
|
);
|
|
expect(canPush).toEqual(false);
|
|
});
|
|
|
|
tap.test('Integration: should access storage backend', async () => {
|
|
const storage = registry.getStorage();
|
|
expect(storage).toBeDefined();
|
|
|
|
// Test basic storage operations
|
|
const testKey = 'test/storage/key';
|
|
const testData = Buffer.from('test data', 'utf-8');
|
|
|
|
await storage.putObject(testKey, testData);
|
|
const retrieved = await storage.getObject(testKey);
|
|
|
|
expect(retrieved).toBeInstanceOf(Buffer);
|
|
expect(retrieved?.toString('utf-8')).toEqual('test data');
|
|
|
|
const exists = await storage.objectExists(testKey);
|
|
expect(exists).toEqual(true);
|
|
|
|
await storage.deleteObject(testKey);
|
|
const existsAfterDelete = await storage.objectExists(testKey);
|
|
expect(existsAfterDelete).toEqual(false);
|
|
});
|
|
|
|
tap.postTask('cleanup registry', async () => {
|
|
if (registry) {
|
|
registry.destroy();
|
|
}
|
|
});
|
|
|
|
export default tap.start();
|