Files
smartregistry/test/test.npm.ts

385 lines
11 KiB
TypeScript
Raw Normal View History

2025-11-19 20:45:37 +00:00
import { expect, tap } from '@git.zone/tstest/tapbundle';
import { SmartRegistry } from '../ts/index.js';
import { streamToBuffer, streamToJson } from '../ts/core/helpers.stream.js';
2025-11-19 20:45:37 +00:00
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);
const body = await streamToJson(response.body);
expect(body).toHaveProperty('token');
expect(body.token).toBeTypeOf('string');
2025-11-19 20:45:37 +00:00
});
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);
const body = await streamToJson(response.body);
expect(body).toHaveProperty('ok');
expect(body.ok).toEqual(true);
2025-11-19 20:45:37 +00:00
});
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);
const body = await streamToJson(response.body);
expect(body).toHaveProperty('name');
expect(body.name).toEqual(testPackageName);
expect(body.versions).toHaveProperty(testVersion);
expect(body['dist-tags'].latest).toEqual(testVersion);
2025-11-19 20:45:37 +00:00
});
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);
const body = await streamToJson(response.body);
expect(body).toHaveProperty('version');
expect(body.version).toEqual(testVersion);
expect(body.name).toEqual(testPackageName);
2025-11-19 20:45:37 +00:00
});
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);
const body = await streamToBuffer(response.body);
expect(body).toBeInstanceOf(Buffer);
expect(body.toString('utf-8')).toEqual('fake tarball content');
2025-11-19 20:45:37 +00:00
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);
const getBody = await streamToJson(getResponse.body);
expect(getBody.versions).toHaveProperty(newVersion);
2025-11-19 20:45:37 +00:00
});
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);
const body = await streamToJson(response.body);
expect(body).toHaveProperty('latest');
expect(body.latest).toBeTypeOf('string');
2025-11-19 20:45:37 +00:00
});
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: {},
});
const getBody2 = await streamToJson(getResponse.body);
expect(getBody2['dist-tags'].beta).toEqual('1.1.0');
2025-11-19 20:45:37 +00:00
});
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: {},
});
const getBody3 = await streamToJson(getResponse.body);
expect(getBody3['dist-tags']).not.toHaveProperty('beta');
2025-11-19 20:45:37 +00:00
});
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);
const body = await streamToJson(response.body);
expect(body).toHaveProperty('token');
expect(body.readonly).toEqual(true);
2025-11-19 20:45:37 +00:00
});
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);
const body = await streamToJson(response.body);
expect(body).toHaveProperty('objects');
expect(body.objects).toBeInstanceOf(Array);
expect(body.objects.length).toBeGreaterThan(0);
2025-11-19 20:45:37 +00:00
});
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);
const body = await streamToJson(response.body);
expect(body).toHaveProperty('objects');
expect(body.objects).toBeInstanceOf(Array);
expect(body.total).toBeGreaterThan(0);
2025-11-19 20:45:37 +00:00
});
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 body = await streamToJson(response.body);
const results = body.objects;
2025-11-19 20:45:37 +00:00
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: {},
});
const getBody4 = await streamToJson(getResponse.body);
expect(getBody4.versions).not.toHaveProperty(testVersion);
2025-11-19 20:45:37 +00:00
});
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);
const body = await streamToJson(response.body);
expect(body).toHaveProperty('error');
2025-11-19 20:45:37 +00:00
});
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);
const body = await streamToJson(response.body);
expect(body).toHaveProperty('error');
2025-11-19 20:45:37 +00:00
});
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();
}
});
2025-11-19 20:45:37 +00:00
export default tap.start();