368 lines
10 KiB
TypeScript
368 lines
10 KiB
TypeScript
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
|
import { SmartRegistry } from '../ts/index.js';
|
|
import { createTestRegistry, createTestTokens, createTestPackument } from './helpers/registry.js';
|
|
|
|
let registry: SmartRegistry;
|
|
let npmToken: string;
|
|
let userId: string;
|
|
|
|
// Test data
|
|
const testPackageName = 'test-package';
|
|
const testVersion = '1.0.0';
|
|
const testTarballData = Buffer.from('fake tarball content', 'utf-8');
|
|
|
|
tap.test('NPM: should create registry instance', async () => {
|
|
registry = await createTestRegistry();
|
|
const tokens = await createTestTokens(registry);
|
|
npmToken = tokens.npmToken;
|
|
userId = tokens.userId;
|
|
|
|
expect(registry).toBeInstanceOf(SmartRegistry);
|
|
expect(npmToken).toBeTypeOf('string');
|
|
});
|
|
|
|
tap.test('NPM: should handle user authentication (PUT /-/user/org.couchdb.user:{user})', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'PUT',
|
|
path: '/npm/-/user/org.couchdb.user:testuser',
|
|
headers: {},
|
|
query: {},
|
|
body: {
|
|
name: 'testuser',
|
|
password: 'testpass',
|
|
},
|
|
});
|
|
|
|
expect(response.status).toEqual(201);
|
|
expect(response.body).toHaveProperty('token');
|
|
expect((response.body as any).token).toBeTypeOf('string');
|
|
});
|
|
|
|
tap.test('NPM: should publish a package (PUT /{package})', async () => {
|
|
const packument = createTestPackument(testPackageName, testVersion, testTarballData);
|
|
|
|
const response = await registry.handleRequest({
|
|
method: 'PUT',
|
|
path: `/npm/${testPackageName}`,
|
|
headers: {
|
|
Authorization: `Bearer ${npmToken}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
query: {},
|
|
body: packument,
|
|
});
|
|
|
|
expect(response.status).toEqual(201);
|
|
expect(response.body).toHaveProperty('ok');
|
|
expect((response.body as any).ok).toEqual(true);
|
|
});
|
|
|
|
tap.test('NPM: should retrieve package metadata (GET /{package})', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: `/npm/${testPackageName}`,
|
|
headers: {},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
expect(response.body).toHaveProperty('name');
|
|
expect((response.body as any).name).toEqual(testPackageName);
|
|
expect((response.body as any).versions).toHaveProperty(testVersion);
|
|
expect((response.body as any)['dist-tags'].latest).toEqual(testVersion);
|
|
});
|
|
|
|
tap.test('NPM: should retrieve specific version metadata (GET /{package}/{version})', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: `/npm/${testPackageName}/${testVersion}`,
|
|
headers: {},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
expect(response.body).toHaveProperty('version');
|
|
expect((response.body as any).version).toEqual(testVersion);
|
|
expect((response.body as any).name).toEqual(testPackageName);
|
|
});
|
|
|
|
tap.test('NPM: should download tarball (GET /{package}/-/{tarball})', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: `/npm/${testPackageName}/-/${testPackageName}-${testVersion}.tgz`,
|
|
headers: {},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
expect(response.body).toBeInstanceOf(Buffer);
|
|
expect((response.body as Buffer).toString('utf-8')).toEqual('fake tarball content');
|
|
expect(response.headers['Content-Type']).toEqual('application/octet-stream');
|
|
});
|
|
|
|
tap.test('NPM: should publish a new version of the package', async () => {
|
|
const newVersion = '1.1.0';
|
|
const newTarballData = Buffer.from('new version tarball', 'utf-8');
|
|
const packument = createTestPackument(testPackageName, newVersion, newTarballData);
|
|
|
|
const response = await registry.handleRequest({
|
|
method: 'PUT',
|
|
path: `/npm/${testPackageName}`,
|
|
headers: {
|
|
Authorization: `Bearer ${npmToken}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
query: {},
|
|
body: packument,
|
|
});
|
|
|
|
expect(response.status).toEqual(201);
|
|
|
|
// Verify the new version is available
|
|
const getResponse = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: `/npm/${testPackageName}`,
|
|
headers: {},
|
|
query: {},
|
|
});
|
|
|
|
expect(getResponse.status).toEqual(200);
|
|
expect((getResponse.body as any).versions).toHaveProperty(newVersion);
|
|
});
|
|
|
|
tap.test('NPM: should get dist-tags (GET /-/package/{pkg}/dist-tags)', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: `/npm/-/package/${testPackageName}/dist-tags`,
|
|
headers: {},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
expect(response.body).toHaveProperty('latest');
|
|
expect((response.body as any).latest).toBeTypeOf('string');
|
|
});
|
|
|
|
tap.test('NPM: should update dist-tag (PUT /-/package/{pkg}/dist-tags/{tag})', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'PUT',
|
|
path: `/npm/-/package/${testPackageName}/dist-tags/beta`,
|
|
headers: {
|
|
Authorization: `Bearer ${npmToken}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
query: {},
|
|
body: '1.1.0',
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
|
|
// Verify the tag was updated
|
|
const getResponse = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: `/npm/${testPackageName}`,
|
|
headers: {},
|
|
query: {},
|
|
});
|
|
|
|
expect((getResponse.body as any)['dist-tags'].beta).toEqual('1.1.0');
|
|
});
|
|
|
|
tap.test('NPM: should delete dist-tag (DELETE /-/package/{pkg}/dist-tags/{tag})', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'DELETE',
|
|
path: `/npm/-/package/${testPackageName}/dist-tags/beta`,
|
|
headers: {
|
|
Authorization: `Bearer ${npmToken}`,
|
|
},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
|
|
// Verify the tag was deleted
|
|
const getResponse = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: `/npm/${testPackageName}`,
|
|
headers: {},
|
|
query: {},
|
|
});
|
|
|
|
expect((getResponse.body as any)['dist-tags']).not.toHaveProperty('beta');
|
|
});
|
|
|
|
tap.test('NPM: should create a new token (POST /-/npm/v1/tokens)', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'POST',
|
|
path: '/npm/-/npm/v1/tokens',
|
|
headers: {
|
|
Authorization: `Bearer ${npmToken}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
query: {},
|
|
body: {
|
|
password: 'testpass',
|
|
readonly: true,
|
|
cidr_whitelist: [],
|
|
},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
expect(response.body).toHaveProperty('token');
|
|
expect((response.body as any).readonly).toEqual(true);
|
|
});
|
|
|
|
tap.test('NPM: should list tokens (GET /-/npm/v1/tokens)', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: '/npm/-/npm/v1/tokens',
|
|
headers: {
|
|
Authorization: `Bearer ${npmToken}`,
|
|
},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
expect(response.body).toHaveProperty('objects');
|
|
expect((response.body as any).objects).toBeInstanceOf(Array);
|
|
expect((response.body as any).objects.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
tap.test('NPM: should search packages (GET /-/v1/search)', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: '/npm/-/v1/search',
|
|
headers: {},
|
|
query: {
|
|
text: 'test',
|
|
size: '20',
|
|
},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
expect(response.body).toHaveProperty('objects');
|
|
expect((response.body as any).objects).toBeInstanceOf(Array);
|
|
expect((response.body as any).total).toBeGreaterThan(0);
|
|
});
|
|
|
|
tap.test('NPM: should search packages with specific query', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: '/npm/-/v1/search',
|
|
headers: {},
|
|
query: {
|
|
text: testPackageName,
|
|
},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
const results = (response.body as any).objects;
|
|
expect(results.length).toBeGreaterThan(0);
|
|
expect(results[0].package.name).toEqual(testPackageName);
|
|
});
|
|
|
|
tap.test('NPM: should unpublish a specific version (DELETE /{package}/-/{version})', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'DELETE',
|
|
path: `/npm/${testPackageName}/-/${testVersion}`,
|
|
headers: {
|
|
Authorization: `Bearer ${npmToken}`,
|
|
},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
|
|
// Verify the version was removed
|
|
const getResponse = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: `/npm/${testPackageName}`,
|
|
headers: {},
|
|
query: {},
|
|
});
|
|
|
|
expect((getResponse.body as any).versions).not.toHaveProperty(testVersion);
|
|
});
|
|
|
|
tap.test('NPM: should unpublish entire package (DELETE /{package}/-rev/{rev})', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'DELETE',
|
|
path: `/npm/${testPackageName}/-rev/1`,
|
|
headers: {
|
|
Authorization: `Bearer ${npmToken}`,
|
|
},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(200);
|
|
|
|
// Verify the package was removed
|
|
const getResponse = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: `/npm/${testPackageName}`,
|
|
headers: {},
|
|
query: {},
|
|
});
|
|
|
|
expect(getResponse.status).toEqual(404);
|
|
});
|
|
|
|
tap.test('NPM: should return 404 for non-existent package', async () => {
|
|
const response = await registry.handleRequest({
|
|
method: 'GET',
|
|
path: '/npm/non-existent-package',
|
|
headers: {},
|
|
query: {},
|
|
});
|
|
|
|
expect(response.status).toEqual(404);
|
|
expect(response.body).toHaveProperty('error');
|
|
});
|
|
|
|
tap.test('NPM: should return 401 for unauthorized publish', async () => {
|
|
const packument = createTestPackument('unauthorized-package', '1.0.0', testTarballData);
|
|
|
|
const response = await registry.handleRequest({
|
|
method: 'PUT',
|
|
path: '/npm/unauthorized-package',
|
|
headers: {
|
|
// No authorization header
|
|
'Content-Type': 'application/json',
|
|
},
|
|
query: {},
|
|
body: packument,
|
|
});
|
|
|
|
expect(response.status).toEqual(401);
|
|
expect(response.body).toHaveProperty('error');
|
|
});
|
|
|
|
tap.test('NPM: should reject readonly token for write operations', async () => {
|
|
// Create a readonly token
|
|
const authManager = registry.getAuthManager();
|
|
const readonlyToken = await authManager.createNpmToken(userId, true);
|
|
|
|
const packument = createTestPackument('readonly-test', '1.0.0', testTarballData);
|
|
|
|
const response = await registry.handleRequest({
|
|
method: 'PUT',
|
|
path: '/npm/readonly-test',
|
|
headers: {
|
|
Authorization: `Bearer ${readonlyToken}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
query: {},
|
|
body: packument,
|
|
});
|
|
|
|
expect(response.status).toEqual(401);
|
|
});
|
|
|
|
tap.postTask('cleanup registry', async () => {
|
|
if (registry) {
|
|
registry.destroy();
|
|
}
|
|
});
|
|
|
|
export default tap.start();
|