nulti registry support
This commit is contained in:
212
test/test.ts
212
test/test.ts
@@ -1,8 +1,214 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import * as smartregistry from '../ts/index.js';
|
||||
import * as qenv from '@push.rocks/qenv';
|
||||
|
||||
tap.test('first test', async () => {
|
||||
console.log(smartregistry);
|
||||
const testQenv = new qenv.Qenv('./', './.nogit');
|
||||
|
||||
let registry: smartregistry.SmartRegistry;
|
||||
let testToken: string;
|
||||
|
||||
tap.test('should create SmartRegistry instance', async () => {
|
||||
// Create mock callbacks for testing
|
||||
const loginCallback: smartregistry.TLoginCallback = async (credentials) => {
|
||||
// Simple mock: return a fake JWT token
|
||||
const tokenPayload = {
|
||||
iss: 'test-registry',
|
||||
sub: credentials.username,
|
||||
aud: 'test-service',
|
||||
exp: Math.floor(Date.now() / 1000) + 3600,
|
||||
nbf: Math.floor(Date.now() / 1000),
|
||||
iat: Math.floor(Date.now() / 1000),
|
||||
access: [
|
||||
{
|
||||
type: 'repository' as const,
|
||||
name: 'test/repo',
|
||||
actions: ['*'] as smartregistry.TRegistryAction[],
|
||||
},
|
||||
],
|
||||
};
|
||||
// In production, this would be a real JWT
|
||||
return JSON.stringify(tokenPayload);
|
||||
};
|
||||
|
||||
const authCallback: smartregistry.TAuthCallback = async (token, repository, action) => {
|
||||
// Simple mock: allow all actions for testing
|
||||
try {
|
||||
const payload = JSON.parse(token);
|
||||
// Check if token has access to the repository
|
||||
const hasAccess = payload.access.some(
|
||||
(acc: any) =>
|
||||
acc.name === repository &&
|
||||
(acc.actions.includes(action) || acc.actions.includes('*'))
|
||||
);
|
||||
return hasAccess;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Read S3 config from env.json
|
||||
const s3AccessKey = await testQenv.getEnvVarOnDemand('S3_ACCESS_KEY');
|
||||
const s3SecretKey = await testQenv.getEnvVarOnDemand('S3_SECRET_KEY');
|
||||
const s3Endpoint = await testQenv.getEnvVarOnDemand('S3_ENDPOINT');
|
||||
const s3Port = await testQenv.getEnvVarOnDemand('S3_PORT');
|
||||
|
||||
const config: smartregistry.IRegistryConfig = {
|
||||
storage: {
|
||||
accessKey: s3AccessKey || 'minioadmin',
|
||||
accessSecret: s3SecretKey || 'minioadmin',
|
||||
endpoint: s3Endpoint || 'localhost',
|
||||
port: parseInt(s3Port || '9000', 10),
|
||||
useSsl: false,
|
||||
region: 'us-east-1',
|
||||
bucketName: 'test-registry',
|
||||
},
|
||||
serviceName: 'test-registry',
|
||||
tokenRealm: 'https://auth.example.com/token',
|
||||
loginCallback,
|
||||
authCallback,
|
||||
};
|
||||
|
||||
registry = new smartregistry.SmartRegistry(config);
|
||||
await registry.init();
|
||||
|
||||
expect(registry).toBeInstanceOf(smartregistry.SmartRegistry);
|
||||
});
|
||||
|
||||
tap.start();
|
||||
tap.test('should login and get token', async () => {
|
||||
testToken = await registry.login({
|
||||
username: 'testuser',
|
||||
password: 'testpass',
|
||||
});
|
||||
|
||||
expect(testToken).toBeTypeOf('string');
|
||||
expect(testToken.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
tap.test('should upload a blob via chunked upload', async () => {
|
||||
const testData = Buffer.from('Hello, OCI Registry!', 'utf-8');
|
||||
const crypto = await import('crypto');
|
||||
const digest = `sha256:${crypto.createHash('sha256').update(testData).digest('hex')}`;
|
||||
|
||||
// Initiate upload
|
||||
const initResult = await registry.initiateUpload('test/repo', testToken);
|
||||
expect(initResult).toHaveProperty('uploadId');
|
||||
|
||||
if ('uploadId' in initResult) {
|
||||
const uploadId = initResult.uploadId;
|
||||
|
||||
// Upload chunk
|
||||
const chunkResult = await registry.uploadChunk(
|
||||
uploadId,
|
||||
testData,
|
||||
`0-${testData.length - 1}`,
|
||||
testToken
|
||||
);
|
||||
expect(chunkResult).toHaveProperty('location');
|
||||
|
||||
// Complete upload
|
||||
const completeResult = await registry.completeUpload(uploadId, digest, testToken);
|
||||
expect(completeResult).toHaveProperty('digest');
|
||||
if ('digest' in completeResult) {
|
||||
expect(completeResult.digest).toEqual(digest);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tap.test('should retrieve a blob', async () => {
|
||||
const testData = Buffer.from('Hello, OCI Registry!', 'utf-8');
|
||||
const crypto = await import('crypto');
|
||||
const digest = `sha256:${crypto.createHash('sha256').update(testData).digest('hex')}`;
|
||||
|
||||
const result = await registry.getBlob('test/repo', digest, testToken);
|
||||
expect(result).toHaveProperty('data');
|
||||
|
||||
if ('data' in result) {
|
||||
expect(result.data.toString('utf-8')).toEqual('Hello, OCI Registry!');
|
||||
}
|
||||
});
|
||||
|
||||
tap.test('should check if blob exists (HEAD)', async () => {
|
||||
const testData = Buffer.from('Hello, OCI Registry!', 'utf-8');
|
||||
const crypto = await import('crypto');
|
||||
const digest = `sha256:${crypto.createHash('sha256').update(testData).digest('hex')}`;
|
||||
|
||||
const result = await registry.headBlob('test/repo', digest, testToken);
|
||||
expect(result).toHaveProperty('exists');
|
||||
|
||||
if ('exists' in result) {
|
||||
expect(result.exists).toEqual(true);
|
||||
expect(result.size).toEqual(testData.length);
|
||||
}
|
||||
});
|
||||
|
||||
tap.test('should upload a manifest', async () => {
|
||||
const testManifest: smartregistry.IOciManifest = {
|
||||
schemaVersion: 2,
|
||||
mediaType: 'application/vnd.oci.image.manifest.v1+json',
|
||||
config: {
|
||||
mediaType: 'application/vnd.oci.image.config.v1+json',
|
||||
size: 123,
|
||||
digest: 'sha256:' + '0'.repeat(64),
|
||||
},
|
||||
layers: [
|
||||
{
|
||||
mediaType: 'application/vnd.oci.image.layer.v1.tar+gzip',
|
||||
size: 456,
|
||||
digest: 'sha256:' + '1'.repeat(64),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = await registry.putManifest(
|
||||
'test/repo',
|
||||
'latest',
|
||||
testManifest,
|
||||
'application/vnd.oci.image.manifest.v1+json',
|
||||
testToken
|
||||
);
|
||||
|
||||
expect(result).toHaveProperty('digest');
|
||||
if ('digest' in result) {
|
||||
expect(result.digest).toMatch(/^sha256:[a-f0-9]{64}$/);
|
||||
}
|
||||
});
|
||||
|
||||
tap.test('should retrieve a manifest by tag', async () => {
|
||||
const result = await registry.getManifest('test/repo', 'latest', testToken);
|
||||
expect(result).toHaveProperty('data');
|
||||
|
||||
if ('data' in result) {
|
||||
const manifest = JSON.parse(result.data.toString('utf-8'));
|
||||
expect(manifest).toHaveProperty('schemaVersion');
|
||||
expect(manifest.schemaVersion).toEqual(2);
|
||||
}
|
||||
});
|
||||
|
||||
tap.test('should list tags', async () => {
|
||||
const result = await registry.listTags('test/repo', testToken);
|
||||
expect(result).toHaveProperty('tags');
|
||||
|
||||
if ('tags' in result) {
|
||||
expect(result.tags).toBeInstanceOf(Array);
|
||||
expect(result.tags).toContain('latest');
|
||||
}
|
||||
});
|
||||
|
||||
tap.test('should generate auth challenge', async () => {
|
||||
const challenge = registry.getAuthChallenge('test/repo', ['pull', 'push']);
|
||||
expect(challenge).toInclude('Bearer');
|
||||
expect(challenge).toInclude('realm=');
|
||||
expect(challenge).toInclude('service=');
|
||||
expect(challenge).toInclude('scope=');
|
||||
});
|
||||
|
||||
tap.test('should handle unauthorized access', async () => {
|
||||
const result = await registry.getBlob('test/repo', 'sha256:invalid', 'invalid-token');
|
||||
expect(result).toHaveProperty('errors');
|
||||
|
||||
if ('errors' in result) {
|
||||
expect(result.errors[0].code).toEqual('DENIED');
|
||||
}
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
|
||||
Reference in New Issue
Block a user