Files
smartregistry/test/test.composer.ts

297 lines
9.1 KiB
TypeScript

import { expect, tap } from '@git.zone/tstest/tapbundle';
import { SmartRegistry } from '../ts/index.js';
import { createTestRegistry, createTestTokens, createComposerZip } from './helpers/registry.js';
let registry: SmartRegistry;
let composerToken: string;
let userId: string;
// Test data
const testPackageName = 'vendor/test-package';
const testVersion = '1.0.0';
let testZipData: Buffer;
tap.test('Composer: should create registry instance', async () => {
registry = await createTestRegistry();
const tokens = await createTestTokens(registry);
composerToken = tokens.composerToken;
userId = tokens.userId;
expect(registry).toBeInstanceOf(SmartRegistry);
expect(composerToken).toBeTypeOf('string');
});
tap.test('Composer: should create test ZIP package', async () => {
testZipData = await createComposerZip(testPackageName, testVersion, {
description: 'Test Composer package for registry',
license: ['MIT'],
authors: [{ name: 'Test Author', email: 'test@example.com' }],
});
expect(testZipData).toBeInstanceOf(Buffer);
expect(testZipData.length).toBeGreaterThan(0);
});
tap.test('Composer: should return packages.json (GET /packages.json)', async () => {
const response = await registry.handleRequest({
method: 'GET',
path: '/composer/packages.json',
headers: {},
query: {},
});
expect(response.status).toEqual(200);
expect(response.body).toHaveProperty('metadata-url');
expect(response.body).toHaveProperty('available-packages');
expect(response.body['available-packages']).toBeInstanceOf(Array);
});
tap.test('Composer: should upload a package (PUT /packages/{vendor/package})', async () => {
const response = await registry.handleRequest({
method: 'PUT',
path: `/composer/packages/${testPackageName}`,
headers: {
Authorization: `Bearer ${composerToken}`,
'Content-Type': 'application/zip',
},
query: {},
body: testZipData,
});
expect(response.status).toEqual(201);
expect(response.body.status).toEqual('success');
expect(response.body.package).toEqual(testPackageName);
expect(response.body.version).toEqual(testVersion);
});
tap.test('Composer: should retrieve package metadata (GET /p2/{vendor/package}.json)', async () => {
const response = await registry.handleRequest({
method: 'GET',
path: `/composer/p2/${testPackageName}.json`,
headers: {},
query: {},
});
expect(response.status).toEqual(200);
expect(response.body).toHaveProperty('packages');
expect(response.body.packages[testPackageName]).toBeInstanceOf(Array);
expect(response.body.packages[testPackageName].length).toEqual(1);
const packageData = response.body.packages[testPackageName][0];
expect(packageData.name).toEqual(testPackageName);
expect(packageData.version).toEqual(testVersion);
expect(packageData.version_normalized).toEqual('1.0.0.0');
expect(packageData).toHaveProperty('dist');
expect(packageData.dist.type).toEqual('zip');
expect(packageData.dist).toHaveProperty('url');
expect(packageData.dist).toHaveProperty('shasum');
expect(packageData.dist).toHaveProperty('reference');
});
tap.test('Composer: should download package ZIP (GET /dists/{vendor/package}/{ref}.zip)', async () => {
// First get metadata to find reference
const metadataResponse = await registry.handleRequest({
method: 'GET',
path: `/composer/p2/${testPackageName}.json`,
headers: {},
query: {},
});
const reference = metadataResponse.body.packages[testPackageName][0].dist.reference;
const response = await registry.handleRequest({
method: 'GET',
path: `/composer/dists/${testPackageName}/${reference}.zip`,
headers: {},
query: {},
});
expect(response.status).toEqual(200);
expect(response.body).toBeInstanceOf(Buffer);
expect(response.headers['Content-Type']).toEqual('application/zip');
expect(response.headers['Content-Disposition']).toContain('attachment');
});
tap.test('Composer: should list packages (GET /packages/list.json)', async () => {
const response = await registry.handleRequest({
method: 'GET',
path: '/composer/packages/list.json',
headers: {},
query: {},
});
expect(response.status).toEqual(200);
expect(response.body).toHaveProperty('packageNames');
expect(response.body.packageNames).toBeInstanceOf(Array);
expect(response.body.packageNames).toContain(testPackageName);
});
tap.test('Composer: should filter package list (GET /packages/list.json?filter=vendor/*)', async () => {
const response = await registry.handleRequest({
method: 'GET',
path: '/composer/packages/list.json',
headers: {},
query: { filter: 'vendor/*' },
});
expect(response.status).toEqual(200);
expect(response.body.packageNames).toBeInstanceOf(Array);
expect(response.body.packageNames).toContain(testPackageName);
});
tap.test('Composer: should prevent duplicate version upload', async () => {
const response = await registry.handleRequest({
method: 'PUT',
path: `/composer/packages/${testPackageName}`,
headers: {
Authorization: `Bearer ${composerToken}`,
'Content-Type': 'application/zip',
},
query: {},
body: testZipData,
});
expect(response.status).toEqual(409);
expect(response.body.status).toEqual('error');
expect(response.body.message).toContain('already exists');
});
tap.test('Composer: should upload a second version', async () => {
const testVersion2 = '1.1.0';
const testZipData2 = await createComposerZip(testPackageName, testVersion2);
const response = await registry.handleRequest({
method: 'PUT',
path: `/composer/packages/${testPackageName}`,
headers: {
Authorization: `Bearer ${composerToken}`,
'Content-Type': 'application/zip',
},
query: {},
body: testZipData2,
});
expect(response.status).toEqual(201);
expect(response.body.status).toEqual('success');
expect(response.body.version).toEqual(testVersion2);
});
tap.test('Composer: should return multiple versions in metadata', async () => {
const response = await registry.handleRequest({
method: 'GET',
path: `/composer/p2/${testPackageName}.json`,
headers: {},
query: {},
});
expect(response.status).toEqual(200);
expect(response.body.packages[testPackageName]).toBeInstanceOf(Array);
expect(response.body.packages[testPackageName].length).toEqual(2);
const versions = response.body.packages[testPackageName].map((p: any) => p.version);
expect(versions).toContain('1.0.0');
expect(versions).toContain('1.1.0');
});
tap.test('Composer: should delete a specific version (DELETE /packages/{vendor/package}/{version})', async () => {
const response = await registry.handleRequest({
method: 'DELETE',
path: `/composer/packages/${testPackageName}/1.0.0`,
headers: {
Authorization: `Bearer ${composerToken}`,
},
query: {},
});
expect(response.status).toEqual(204);
// Verify version was removed
const metadataResponse = await registry.handleRequest({
method: 'GET',
path: `/composer/p2/${testPackageName}.json`,
headers: {},
query: {},
});
expect(metadataResponse.body.packages[testPackageName].length).toEqual(1);
expect(metadataResponse.body.packages[testPackageName][0].version).toEqual('1.1.0');
});
tap.test('Composer: should require auth for package upload', async () => {
const testZipData3 = await createComposerZip('vendor/unauth-package', '1.0.0');
const response = await registry.handleRequest({
method: 'PUT',
path: '/composer/packages/vendor/unauth-package',
headers: {
'Content-Type': 'application/zip',
},
query: {},
body: testZipData3,
});
expect(response.status).toEqual(401);
expect(response.body.status).toEqual('error');
});
tap.test('Composer: should reject invalid ZIP (no composer.json)', async () => {
const invalidZip = Buffer.from('invalid zip content');
const response = await registry.handleRequest({
method: 'PUT',
path: `/composer/packages/${testPackageName}`,
headers: {
Authorization: `Bearer ${composerToken}`,
'Content-Type': 'application/zip',
},
query: {},
body: invalidZip,
});
expect(response.status).toEqual(400);
expect(response.body.status).toEqual('error');
expect(response.body.message).toContain('composer.json');
});
tap.test('Composer: should delete entire package (DELETE /packages/{vendor/package})', async () => {
const response = await registry.handleRequest({
method: 'DELETE',
path: `/composer/packages/${testPackageName}`,
headers: {
Authorization: `Bearer ${composerToken}`,
},
query: {},
});
expect(response.status).toEqual(204);
// Verify package was removed
const metadataResponse = await registry.handleRequest({
method: 'GET',
path: `/composer/p2/${testPackageName}.json`,
headers: {},
query: {},
});
expect(metadataResponse.status).toEqual(404);
});
tap.test('Composer: should return 404 for non-existent package', async () => {
const response = await registry.handleRequest({
method: 'GET',
path: '/composer/p2/non/existent.json',
headers: {},
query: {},
});
expect(response.status).toEqual(404);
});
tap.postTask('cleanup registry', async () => {
if (registry) {
registry.destroy();
}
});
export default tap.start();