Files
cnpmcore/test/port/controller/package/UpdatePackageController.test.ts

516 lines
16 KiB
TypeScript

import assert from 'node:assert/strict';
import { app, mock } from '@eggjs/mock/bootstrap';
import { RegistryType } from '../../../../app/common/enum/Registry.js';
import { RegistryManagerService } from '../../../../app/core/service/RegistryManagerService.js';
import { TestUtil, type TestUser } from '../../../../test/TestUtil.js';
describe('test/port/controller/package/UpdatePackageController.test.ts', () => {
let publisher: TestUser;
beforeEach(async () => {
publisher = await TestUtil.createUser();
});
describe('[PUT /:fullname/-rev/:rev] update()', () => {
const scopedName = '@cnpm/testmodule-update-package';
let rev = '';
beforeEach(async () => {
const pkg = await TestUtil.getFullPackage({ name: scopedName });
const res = await app
.httpRequest()
.put(`/${pkg.name}`)
.set('authorization', publisher.authorization)
.set('user-agent', publisher.ua)
.send(pkg)
.expect(201);
assert.equal(res.body.ok, true);
assert.match(res.body.rev, /^\d+-\w{24}$/);
rev = res.body.rev;
});
it('should 404 when pkg not exists', async () => {
const user = await TestUtil.createUser();
mock(app.config.cnpmcore, 'admins', { [user.name]: user.email });
const res = await app
.httpRequest()
.put('/banana/-rev/123')
.set('authorization', user.authorization)
.set('user-agent', publisher.ua)
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: user.name, email: user.email }],
});
assert.equal(res.statusCode, 404);
});
it('should 422 when maintainters empty', async () => {
const res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', publisher.authorization)
.set('user-agent', publisher.ua)
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [],
})
.expect(422);
assert.equal(
res.body.error,
'[INVALID_PARAM] maintainers: must NOT have fewer than 1 items'
);
});
it('should 422 when some maintainters not exists', async () => {
const res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', publisher.authorization)
.set('user-agent', publisher.ua)
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [
{
name: 'foo',
email: 'foo@bar.com',
},
],
})
.expect(422);
assert.equal(
res.body.error,
'[UNPROCESSABLE_ENTITY] Maintainer "foo" not exists'
);
});
it('should 403 request user is not maintainer', async () => {
const user = await TestUtil.createUser();
const res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', user.authorization)
.set('user-agent', publisher.ua)
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: user.name, email: user.email }],
})
.expect(403);
assert.equal(
res.body.error,
`[FORBIDDEN] "${user.name}" not authorized to modify ${scopedName}, please contact maintainers: "${publisher.name}"`
);
});
it('should 200 request when user is admin and user is not maintainer', async () => {
const user = await TestUtil.createUser();
mock(app.config.cnpmcore, 'admins', { [user.name]: user.email });
const res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', user.authorization)
.set('user-agent', publisher.ua)
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: user.name, email: user.email }],
})
.expect(200);
assert.equal(res.statusCode, 200);
assert.deepEqual(res.body, { ok: true });
});
it('should 200 when without userPrefix', async () => {
const user = await TestUtil.createUser();
await TestUtil.createUser({
name: 'dnpm:banana',
});
const registryManagerService = await app.getEggObject(
RegistryManagerService
);
const registry = await registryManagerService.createRegistry({
name: 'dnpmcore',
changeStream: 'https://d.cnpmjs.org/_changes',
host: 'https://registry.dnpmmirror.com',
userPrefix: 'dnpm:',
type: RegistryType.Cnpmcore,
});
await TestUtil.createPackage({
name: '@cnpm/banana',
isPrivate: false,
registryId: registry.registryId,
});
mock(app.config.cnpmcore, 'admins', { [user.name]: user.email });
const updateRes = await app
.httpRequest()
.put('/@cnpm/banana/-rev/1')
.set('authorization', user.authorization)
.set('user-agent', publisher.ua)
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: 'banana', email: user.email }],
});
assert.equal(updateRes.statusCode, 200);
assert.deepEqual(updateRes.body, { ok: true });
});
it('should 400 when npm-command invalid', async () => {
const user = await TestUtil.createUser();
let res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', user.authorization)
.set('user-agent', publisher.ua)
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: user.name, email: user.email }],
})
.expect(400);
assert.equal(
res.body.error,
'[BAD_REQUEST] header: npm-command expected "owner", but got ""'
);
res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', user.authorization)
.set('user-agent', publisher.ua)
.set('npm-command', 'adduser')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: user.name, email: user.email }],
})
.expect(400);
assert.equal(
res.body.error,
'[BAD_REQUEST] header: npm-command expected "owner", but got "adduser"'
);
// npm@6: referer: 'xxx [REDACTED]'
// npm@>=7: 'npm-command': 'xxx'
// when npm version < 7, npm command can get from referer
res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', user.authorization)
.set('user-agent', 'npm/6.3.1')
.set('referer', 'addUser add someone [REDACTED]')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: user.name, email: user.email }],
})
.expect(400);
assert.equal(
res.body.error,
'[BAD_REQUEST] header: npm-command expected "owner", but got "addUser"'
);
});
it('should 200 when npm command is npm owner add', async () => {
mock(app.config.cnpmcore, 'enableNpmClientAndVersionCheck', false);
// npm@6: referer: 'xxx [REDACTED]'
// npm@>=7: 'npm-command': 'xxx'
// npm version < 7
const user = await TestUtil.createUser();
let res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', publisher.authorization)
.set('user-agent', 'npm/6.3.1')
.set('referer', 'owner add someone [REDACTED]')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: user.name, email: user.email }],
})
.expect(200);
assert.equal(res.statusCode, 200);
assert.deepEqual(res.body, { ok: true });
// npm version >= 7
res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', user.authorization)
.set('user-agent', 'npm/7.3.1')
.set('referer', '')
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: user.name, email: user.email }],
})
.expect(200);
assert.equal(res.statusCode, 200);
assert.deepEqual(res.body, { ok: true });
});
it('should support pnpm client', async () => {
const user = await TestUtil.createUser();
mock(app.config.cnpmcore, 'admins', { [user.name]: user.email });
const res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', user.authorization)
.set('user-agent', 'pnpm/7.3.1 npm/?')
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: user.name, email: user.email }],
});
assert.deepEqual(res.body, { ok: true });
assert.equal(res.statusCode, 200);
});
it('should 403 when npm client invalid', async () => {
const user = await TestUtil.createUser();
let res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', user.authorization)
.set('user-agent', '')
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: user.name, email: user.email }],
})
.expect(403);
assert.equal(
res.body.error,
'[FORBIDDEN] Only allow npm client to access'
);
res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', user.authorization)
.set('user-agent', 'npm/6.3.1')
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: user.name, email: user.email }],
})
.expect(403);
assert.equal(
res.body.error,
'[FORBIDDEN] Only allow npm@>=7.0.0 client to access'
);
});
it('should 200 when enableNpmClientAndVersionCheck is false', async () => {
mock(app.config.cnpmcore, 'enableNpmClientAndVersionCheck', false);
const user = await TestUtil.createUser();
mock(app.config.cnpmcore, 'admins', { [user.name]: user.email });
let res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', user.authorization)
.set('user-agent', '')
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: user.name, email: user.email }],
})
.expect(200);
assert.equal(res.statusCode, 200);
assert.deepEqual(res.body, { ok: true });
res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', user.authorization)
.set('user-agent', 'npm/6.3.1')
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: user.name, email: user.email }],
})
.expect(200);
assert.equal(res.statusCode, 200);
assert.deepEqual(res.body, { ok: true });
});
it('should 403 when enableNpmClientAndVersionCheck is true', async () => {
mock(app.config.cnpmcore, 'enableNpmClientAndVersionCheck', true);
const user = await TestUtil.createUser();
mock(app.config.cnpmcore, 'admins', { [user.name]: user.email });
let res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', user.authorization)
.set('user-agent', '')
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: user.name, email: user.email }],
})
.expect(403);
assert.equal(res.statusCode, 403);
assert.equal(
res.body.error,
'[FORBIDDEN] Only allow npm client to access'
);
res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', user.authorization)
.set('user-agent', 'npm/6.3.1')
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: user.name, email: user.email }],
})
.expect(403);
assert.equal(res.statusCode, 403);
assert.equal(
res.body.error,
'[FORBIDDEN] Only allow npm@>=7.0.0 client to access'
);
});
it('should 200 and get latest maintainers', async () => {
let res = await app.httpRequest().get(`/${scopedName}`).expect(200);
assert.equal(res.body.maintainers.length, 1);
const user = await TestUtil.createUser();
const user2 = await TestUtil.createUser();
res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', publisher.authorization)
.set('user-agent', publisher.ua)
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [
{ name: user.name, email: user.email },
{ name: user2.name, email: user2.email },
{ name: publisher.name, email: publisher.email },
],
})
.expect(200);
assert.equal(res.body.ok, true);
res = await app.httpRequest().get(`/${scopedName}`).expect(200);
assert.equal(res.body.maintainers.length, 3);
res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', publisher.authorization)
.set('user-agent', publisher.ua)
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [
{ name: user.name, email: user.email },
{ name: user2.name, email: user2.email },
],
})
.expect(200);
assert.equal(res.body.ok, true);
res = await app.httpRequest().get(`/${scopedName}`).expect(200);
assert.equal(res.body.maintainers.length, 2);
// publisher is remove from maintainers
res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', publisher.authorization)
.set('user-agent', publisher.ua)
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: publisher.name, email: publisher.email }],
})
.expect(403);
assert.equal(
res.body.error,
`[FORBIDDEN] "${publisher.name}" not authorized to modify ${scopedName}, please contact maintainers: "${user.name}, ${user2.name}"`
);
});
it('should support pnpm and other npm clients', async () => {
const user = await TestUtil.createUser();
let res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', user.authorization)
.set('user-agent', 'pnpm/7.0.0 npm/6.3.1')
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [{ name: user.name, email: user.email }],
})
.expect(403);
assert.equal(
res.body.error,
'[FORBIDDEN] Only allow npm@>=7.0.0 client to access'
);
// should valid with pnpm6 and npm>10
res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', publisher.authorization)
.set('user-agent', 'pnpm/6.0.0 npm/17.1.0')
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [
{ name: user.name, email: user.email },
{ name: publisher.name, email: publisher.email },
],
})
.expect(200);
assert.equal(res.body.ok, true);
// should valid with pnpm10 and npm=?
res = await app
.httpRequest()
.put(`/${scopedName}/-rev/${rev}`)
.set('authorization', publisher.authorization)
.set('user-agent', 'pnpm/10.0.0 npm/?')
.set('npm-command', 'owner')
.send({
_id: rev,
_rev: rev,
maintainers: [
{ name: user.name, email: user.email },
{ name: publisher.name, email: publisher.email },
],
})
.expect(200);
assert.equal(res.body.ok, true);
});
});
});