298 lines
8.9 KiB
TypeScript
298 lines
8.9 KiB
TypeScript
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
|
import { SmartRegistry } from '../ts/index.js';
|
|
import { createTestRegistry, createTestTokens, calculateDigest, createTestManifest } from './test.helper.js';
|
|
|
|
let registry: SmartRegistry;
|
|
let ociToken: string;
|
|
let testBlobDigest: string;
|
|
let testConfigDigest: string;
|
|
let testManifestDigest: string;
|
|
|
|
// Test data
|
|
const testBlobData = Buffer.from('Hello from OCI test blob!', 'utf-8');
|
|
const testConfigData = Buffer.from(JSON.stringify({ arch: 'amd64', os: 'linux' }), 'utf-8');
|
|
|
|
tap.test('OCI: should create registry instance', async () => {
|
|
registry = await createTestRegistry();
|
|
const tokens = await createTestTokens(registry);
|
|
ociToken = tokens.ociToken;
|
|
|
|
expect(registry).toBeInstanceOf(SmartRegistry);
|
|
expect(ociToken).toBeTypeOf('string');
|
|
});
|
|
|
|
tap.test('OCI: should handle version check (GET /v2/)', 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('OCI: should initiate blob upload (POST /v2/{name}/blobs/uploads/)', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'POST',
|
|
path: '/oci/v2/test-repo/blobs/uploads/',
|
|
headers: {
|
|
Authorization: `Bearer ${ociToken}`,
|
|
},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(202);
|
|
expect(response.headers).toHaveProperty('Location');
|
|
expect(response.headers).toHaveProperty('Docker-Upload-UUID');
|
|
});
|
|
|
|
tap.test('OCI: should upload blob in single PUT', async () => {
|
|
testBlobDigest = calculateDigest(testBlobData);
|
|
|
|
const response = await registry.handleRequest({
|
|
method: 'POST',
|
|
path: '/oci/v2/test-repo/blobs/uploads/',
|
|
headers: {
|
|
Authorization: `Bearer ${ociToken}`,
|
|
},
|
|
query: {
|
|
digest: testBlobDigest,
|
|
},
|
|
body: testBlobData,
|
|
});
|
|
|
|
expect(response.status).toEqual(201);
|
|
expect(response.headers).toHaveProperty('Location');
|
|
expect(response.headers['Docker-Content-Digest']).toEqual(testBlobDigest);
|
|
});
|
|
|
|
tap.test('OCI: should upload config blob', async () => {
|
|
testConfigDigest = calculateDigest(testConfigData);
|
|
|
|
const response = await registry.handleRequest({
|
|
method: 'POST',
|
|
path: '/oci/v2/test-repo/blobs/uploads/',
|
|
headers: {
|
|
Authorization: `Bearer ${ociToken}`,
|
|
},
|
|
query: {
|
|
digest: testConfigDigest,
|
|
},
|
|
body: testConfigData,
|
|
});
|
|
|
|
expect(response.status).toEqual(201);
|
|
expect(response.headers['Docker-Content-Digest']).toEqual(testConfigDigest);
|
|
});
|
|
|
|
tap.test('OCI: should check if blob exists (HEAD /v2/{name}/blobs/{digest})', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'HEAD',
|
|
path: `/oci/v2/test-repo/blobs/${testBlobDigest}`,
|
|
headers: {
|
|
Authorization: `Bearer ${ociToken}`,
|
|
},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
expect(response.headers['Content-Length']).toEqual(testBlobData.length.toString());
|
|
expect(response.headers['Docker-Content-Digest']).toEqual(testBlobDigest);
|
|
});
|
|
|
|
tap.test('OCI: should retrieve blob (GET /v2/{name}/blobs/{digest})', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: `/oci/v2/test-repo/blobs/${testBlobDigest}`,
|
|
headers: {
|
|
Authorization: `Bearer ${ociToken}`,
|
|
},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
expect(response.body).toBeInstanceOf(Buffer);
|
|
expect((response.body as Buffer).toString('utf-8')).toEqual('Hello from OCI test blob!');
|
|
expect(response.headers['Docker-Content-Digest']).toEqual(testBlobDigest);
|
|
});
|
|
|
|
tap.test('OCI: should upload manifest (PUT /v2/{name}/manifests/{reference})', async () => {
|
|
const manifest = createTestManifest(testConfigDigest, testBlobDigest);
|
|
const manifestJson = JSON.stringify(manifest);
|
|
const manifestBuffer = Buffer.from(manifestJson, 'utf-8');
|
|
testManifestDigest = calculateDigest(manifestBuffer);
|
|
|
|
const response = await registry.handleRequest({
|
|
method: 'PUT',
|
|
path: '/oci/v2/test-repo/manifests/v1.0.0',
|
|
headers: {
|
|
Authorization: `Bearer ${ociToken}`,
|
|
'Content-Type': 'application/vnd.oci.image.manifest.v1+json',
|
|
},
|
|
query: {},
|
|
body: manifestBuffer,
|
|
});
|
|
|
|
expect(response.status).toEqual(201);
|
|
expect(response.headers).toHaveProperty('Location');
|
|
expect(response.headers['Docker-Content-Digest']).toMatch(/^sha256:[a-f0-9]{64}$/);
|
|
});
|
|
|
|
tap.test('OCI: should retrieve manifest by tag (GET /v2/{name}/manifests/{reference})', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: '/oci/v2/test-repo/manifests/v1.0.0',
|
|
headers: {
|
|
Authorization: `Bearer ${ociToken}`,
|
|
Accept: 'application/vnd.oci.image.manifest.v1+json',
|
|
},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
expect(response.body).toBeInstanceOf(Buffer);
|
|
|
|
const manifest = JSON.parse((response.body as Buffer).toString('utf-8'));
|
|
expect(manifest.schemaVersion).toEqual(2);
|
|
expect(manifest.config.digest).toEqual(testConfigDigest);
|
|
expect(manifest.layers[0].digest).toEqual(testBlobDigest);
|
|
});
|
|
|
|
tap.test('OCI: should retrieve manifest by digest (GET /v2/{name}/manifests/{digest})', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: `/oci/v2/test-repo/manifests/${testManifestDigest}`,
|
|
headers: {
|
|
Authorization: `Bearer ${ociToken}`,
|
|
Accept: 'application/vnd.oci.image.manifest.v1+json',
|
|
},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
expect(response.headers['Docker-Content-Digest']).toEqual(testManifestDigest);
|
|
});
|
|
|
|
tap.test('OCI: should check if manifest exists (HEAD /v2/{name}/manifests/{reference})', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'HEAD',
|
|
path: '/oci/v2/test-repo/manifests/v1.0.0',
|
|
headers: {
|
|
Authorization: `Bearer ${ociToken}`,
|
|
Accept: 'application/vnd.oci.image.manifest.v1+json',
|
|
},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
expect(response.headers['Docker-Content-Digest']).toMatch(/^sha256:[a-f0-9]{64}$/);
|
|
});
|
|
|
|
tap.test('OCI: should list tags (GET /v2/{name}/tags/list)', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: '/oci/v2/test-repo/tags/list',
|
|
headers: {
|
|
Authorization: `Bearer ${ociToken}`,
|
|
},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
expect(response.body).toHaveProperty('tags');
|
|
|
|
const tagList = response.body as any;
|
|
expect(tagList.name).toEqual('test-repo');
|
|
expect(tagList.tags).toBeInstanceOf(Array);
|
|
expect(tagList.tags).toContain('v1.0.0');
|
|
});
|
|
|
|
tap.test('OCI: should handle pagination for tag list', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: '/oci/v2/test-repo/tags/list',
|
|
headers: {
|
|
Authorization: `Bearer ${ociToken}`,
|
|
},
|
|
query: {
|
|
n: '10',
|
|
},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
expect(response.body).toHaveProperty('tags');
|
|
});
|
|
|
|
tap.test('OCI: should return 404 for non-existent blob', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: '/oci/v2/test-repo/blobs/sha256:0000000000000000000000000000000000000000000000000000000000000000',
|
|
headers: {
|
|
Authorization: `Bearer ${ociToken}`,
|
|
},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(404);
|
|
expect(response.body).toHaveProperty('errors');
|
|
});
|
|
|
|
tap.test('OCI: should return 404 for non-existent manifest', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: '/oci/v2/test-repo/manifests/non-existent-tag',
|
|
headers: {
|
|
Authorization: `Bearer ${ociToken}`,
|
|
Accept: 'application/vnd.oci.image.manifest.v1+json',
|
|
},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(404);
|
|
expect(response.body).toHaveProperty('errors');
|
|
});
|
|
|
|
tap.test('OCI: should delete manifest (DELETE /v2/{name}/manifests/{digest})', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'DELETE',
|
|
path: `/oci/v2/test-repo/manifests/${testManifestDigest}`,
|
|
headers: {
|
|
Authorization: `Bearer ${ociToken}`,
|
|
},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(202);
|
|
});
|
|
|
|
tap.test('OCI: should delete blob (DELETE /v2/{name}/blobs/{digest})', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'DELETE',
|
|
path: `/oci/v2/test-repo/blobs/${testBlobDigest}`,
|
|
headers: {
|
|
Authorization: `Bearer ${ociToken}`,
|
|
},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(202);
|
|
});
|
|
|
|
tap.test('OCI: should handle unauthorized requests', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: '/oci/v2/test-repo/manifests/v1.0.0',
|
|
headers: {
|
|
// No authorization header
|
|
},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(401);
|
|
expect(response.headers['WWW-Authenticate']).toInclude('Bearer');
|
|
});
|
|
|
|
export default tap.start();
|