npq 3.10.1 → 3.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -1
- package/.editorconfig +0 -49
- package/.github/ISSUE_TEMPLATE.md +0 -32
- package/.github/Logo Horizontal.png +0 -0
- package/.github/Logo Vertical.png +0 -0
- package/.github/Logo.png +0 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -35
- package/.github/npq-demo-1.gif +0 -0
- package/.github/npq-demo-1.png +0 -0
- package/.github/npq-demo.gif +0 -0
- package/.github/npq.mov +0 -0
- package/.github/npq.mp4 +0 -0
- package/.github/npq.png +0 -0
- package/.github/workflows/automerge.yml +0 -39
- package/.github/workflows/main.yml +0 -58
- package/.husky/commit-msg +0 -4
- package/.husky/post-merge +0 -4
- package/.husky/pre-commit +0 -4
- package/.husky/pre-push +0 -4
- package/.prettierrc.js +0 -9
- package/CHANGELOG.md +0 -27
- package/CODE_OF_CONDUCT.md +0 -73
- package/CONTRIBUTING.md +0 -55
- package/SECURITY.md +0 -23
- package/__tests__/__fixtures__/test.marshall.js +0 -58
- package/__tests__/cli.parser.test.js +0 -121
- package/__tests__/cli.test.js +0 -110
- package/__tests__/cliPrompt.test.js +0 -409
- package/__tests__/env-var-integration.test.js +0 -109
- package/__tests__/marshalls.base.test.js +0 -113
- package/__tests__/marshalls.classMethods.test.js +0 -34
- package/__tests__/marshalls.expiredDomains.test.js +0 -100
- package/__tests__/marshalls.newBin.test.js +0 -441
- package/__tests__/marshalls.provenance.test.js +0 -383
- package/__tests__/marshalls.repo.test.js +0 -126
- package/__tests__/marshalls.signatures.test.js +0 -132
- package/__tests__/marshalls.snyk.test.js +0 -294
- package/__tests__/marshalls.tasks.test.js +0 -130
- package/__tests__/marshalls.typosquatting.test.js +0 -76
- package/__tests__/marshalls.version-maturity.test.js +0 -194
- package/__tests__/mocks/registryPackageOk.mock.json +0 -146
- package/__tests__/mocks/registryPackageUnpublished.mock.json +0 -31
- package/__tests__/packageManager.test.js +0 -93
- package/__tests__/packageRepoUtils.test.js +0 -251
- package/__tests__/reportResults.test.js +0 -772
- package/__tests__/scripts.test.js +0 -146
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
const TestMarshall = require('./__fixtures__/test.marshall')
|
|
2
|
-
const TEST_MARSHALL_NAME = 'test.marshall'
|
|
3
|
-
const BaseMarshall = require('../lib/marshalls/baseMarshall')
|
|
4
|
-
|
|
5
|
-
test('base marshall implemented isEnabled', async () => {
|
|
6
|
-
const testMarshall = new TestMarshall({
|
|
7
|
-
packageRepoUtils: null
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
expect(testMarshall.isEnabled()).toBeTruthy()
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
test('checkPackage returns validation data if it was a success', async () => {
|
|
14
|
-
const testMarshall = new TestMarshall({
|
|
15
|
-
packageRepoUtils: null
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
const ctx = {
|
|
19
|
-
marshalls: {
|
|
20
|
-
[TEST_MARSHALL_NAME]: {
|
|
21
|
-
data: {}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const result = await testMarshall.checkPackage({ packageName: 'express' }, ctx, {})
|
|
27
|
-
expect(result).toEqual('validation-result')
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
test('checkPackage sets the error property if the validation failed', async () => {
|
|
31
|
-
const testMarshall = new TestMarshall({
|
|
32
|
-
packageRepoUtils: null
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
const pkg = {
|
|
36
|
-
packageString: 'trojan'
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const ctx = {
|
|
40
|
-
marshalls: {
|
|
41
|
-
[TEST_MARSHALL_NAME]: {
|
|
42
|
-
data: {}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
testMarshall.init(ctx)
|
|
48
|
-
await testMarshall.checkPackage(pkg, ctx, {})
|
|
49
|
-
expect(ctx.marshalls[TEST_MARSHALL_NAME].errors[0].pkg).toEqual(pkg.packageString)
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
test('setError sets the errors properly', () => {
|
|
53
|
-
const testMarshall = new TestMarshall({
|
|
54
|
-
packageRepoUtils: null
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
const ctx = {
|
|
58
|
-
marshalls: {
|
|
59
|
-
[TEST_MARSHALL_NAME]: {
|
|
60
|
-
data: {}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
const err = {
|
|
65
|
-
pkg: 'test',
|
|
66
|
-
message: 'error message'
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
testMarshall.init(ctx)
|
|
70
|
-
testMarshall.setMessage(err)
|
|
71
|
-
expect(ctx.marshalls[TEST_MARSHALL_NAME].errors.length).toEqual(1)
|
|
72
|
-
expect(ctx.marshalls[TEST_MARSHALL_NAME].errors[0]).toEqual(err)
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
test('setWarning sets the warnings properly', () => {
|
|
76
|
-
const testMarshall = new TestMarshall({
|
|
77
|
-
packageRepoUtils: null
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
const ctx = {
|
|
81
|
-
marshalls: {
|
|
82
|
-
[TEST_MARSHALL_NAME]: {
|
|
83
|
-
data: {}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
const warn = {
|
|
88
|
-
pkg: 'test',
|
|
89
|
-
message: 'warning message'
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
testMarshall.init(ctx)
|
|
93
|
-
|
|
94
|
-
testMarshall.setMessage(warn, true)
|
|
95
|
-
expect(ctx.marshalls[TEST_MARSHALL_NAME].warnings.length).toEqual(1)
|
|
96
|
-
expect(ctx.marshalls[TEST_MARSHALL_NAME].warnings[0]).toEqual(warn)
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
test('base marshall implemented isEnabled', async () => {
|
|
100
|
-
const testMarshall = new BaseMarshall({
|
|
101
|
-
packageRepoUtils: null
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
testMarshall.validate = jest.fn(() => {
|
|
105
|
-
return Promise.reject(new Error('some mock error'))
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
const ctx = { pkgs: ['pkg1'], marshalls: {} }
|
|
109
|
-
const task = {}
|
|
110
|
-
testMarshall.init(ctx, task)
|
|
111
|
-
const result = await testMarshall.run(ctx, task)
|
|
112
|
-
expect(result).toStrictEqual([undefined])
|
|
113
|
-
})
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
beforeEach(() => {
|
|
2
|
-
jest.resetModules()
|
|
3
|
-
})
|
|
4
|
-
|
|
5
|
-
test('collecting marshalls resolves with files array', async () => {
|
|
6
|
-
const marshalls = require('../lib/marshalls')
|
|
7
|
-
|
|
8
|
-
const marshallFiles = await marshalls.collectMarshalls()
|
|
9
|
-
expect(marshallFiles.length).toBeGreaterThan(0)
|
|
10
|
-
expect(marshallFiles.every((file) => file.endsWith('.marshall.js'))).toBe(true)
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
test('collecting marshalls handles directory read errors', async () => {
|
|
14
|
-
const fs = require('node:fs')
|
|
15
|
-
const marshalls = require('../lib/marshalls')
|
|
16
|
-
|
|
17
|
-
// Mock fs.promises.readdir to throw an error
|
|
18
|
-
const originalReaddir = fs.promises.readdir
|
|
19
|
-
fs.promises.readdir = jest.fn().mockRejectedValue(new Error('Directory read error'))
|
|
20
|
-
|
|
21
|
-
await expect(marshalls.collectMarshalls()).rejects.toThrow('Directory read error')
|
|
22
|
-
|
|
23
|
-
// Restore original function
|
|
24
|
-
fs.promises.readdir = originalReaddir
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
test('build marshalls without any should throw error', () => {
|
|
28
|
-
const marshalls = require('../lib/marshalls')
|
|
29
|
-
|
|
30
|
-
expect(marshalls.buildMarshallTasks([])).rejects.toEqual(expect.any(Error))
|
|
31
|
-
expect(marshalls.buildMarshallTasks({})).rejects.toEqual(expect.any(Error))
|
|
32
|
-
expect(marshalls.buildMarshallTasks(5)).rejects.toEqual(expect.any(Error))
|
|
33
|
-
expect(marshalls.buildMarshallTasks('something')).rejects.toEqual(expect.any(Error))
|
|
34
|
-
})
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
const ExpiredDomainsMarshall = require('../lib/marshalls/expiredDomains.marshall')
|
|
2
|
-
|
|
3
|
-
const testMarshall = new ExpiredDomainsMarshall({
|
|
4
|
-
packageRepoUtils: {
|
|
5
|
-
getPackageInfo: (pkgInfo) => {
|
|
6
|
-
return new Promise((resolve) => {
|
|
7
|
-
resolve(pkgInfo)
|
|
8
|
-
})
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
describe('Expired domains test suites', () => {
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
jest.clearAllMocks()
|
|
16
|
-
jest.resetAllMocks()
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
test('has the right title', async () => {
|
|
20
|
-
expect(testMarshall.title()).toEqual('Detecting expired domains for authors account...')
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
test('throws the right error when an email domain cant be verified', async () => {
|
|
24
|
-
const nonExistentEmailAddress =
|
|
25
|
-
'liran.tal+test1234@somenonexistentdomainongoogle109dubv98g39ujasdasda.com'
|
|
26
|
-
const pkgData = {
|
|
27
|
-
packageName: {
|
|
28
|
-
'dist-tags': {
|
|
29
|
-
latest: '1.0.0'
|
|
30
|
-
},
|
|
31
|
-
versions: {
|
|
32
|
-
'1.0.0': {
|
|
33
|
-
maintainers: [
|
|
34
|
-
{ name: 'lirantal', email: 'liran.tal@gmail.com' },
|
|
35
|
-
{
|
|
36
|
-
name: 'lirantal_test_user',
|
|
37
|
-
email: nonExistentEmailAddress
|
|
38
|
-
}
|
|
39
|
-
]
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
await expect(testMarshall.validate(pkgData)).rejects.toThrow(
|
|
46
|
-
/Detected expired domain can be abused for account takeover/
|
|
47
|
-
)
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
test('if email hostname is empty then show an unknown message', async () => {
|
|
51
|
-
const nonExistentEmailAddress = ''
|
|
52
|
-
const pkgData = {
|
|
53
|
-
packageName: {
|
|
54
|
-
'dist-tags': {
|
|
55
|
-
latest: '1.0.0'
|
|
56
|
-
},
|
|
57
|
-
versions: {
|
|
58
|
-
'1.0.0': {
|
|
59
|
-
maintainers: [
|
|
60
|
-
{ name: 'lirantal', email: 'liran.tal@gmail.com' },
|
|
61
|
-
{
|
|
62
|
-
name: 'lirantal_test_user',
|
|
63
|
-
email: nonExistentEmailAddress
|
|
64
|
-
}
|
|
65
|
-
]
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
await expect(testMarshall.validate(pkgData)).rejects.toThrow(
|
|
72
|
-
/Detected expired domain can be abused for account takeover: <unknown>/
|
|
73
|
-
)
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
test('does not throw any errors if the domain resolves well', async () => {
|
|
77
|
-
jest.setTimeout(15000)
|
|
78
|
-
|
|
79
|
-
const pkgData = {
|
|
80
|
-
packageName: {
|
|
81
|
-
'dist-tags': {
|
|
82
|
-
latest: '1.0.0'
|
|
83
|
-
},
|
|
84
|
-
versions: {
|
|
85
|
-
'1.0.0': {
|
|
86
|
-
maintainers: [
|
|
87
|
-
{ name: 'lirantal', email: 'liran.tal@gmail.com' },
|
|
88
|
-
{
|
|
89
|
-
name: 'lirantal_test_user',
|
|
90
|
-
email: 'liran.tal@gmail.com'
|
|
91
|
-
}
|
|
92
|
-
]
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
await expect(testMarshall.validate(pkgData)).resolves.toEqual(expect.anything())
|
|
99
|
-
})
|
|
100
|
-
})
|
|
@@ -1,441 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const NewBinMarshall = require('../lib/marshalls/newbin.marshall')
|
|
4
|
-
const PackageRepoUtils = require('../lib/helpers/packageRepoUtils')
|
|
5
|
-
|
|
6
|
-
jest.mock('../lib/helpers/packageRepoUtils')
|
|
7
|
-
|
|
8
|
-
describe('NewBinMarshall', () => {
|
|
9
|
-
let packageRepoUtilsMock
|
|
10
|
-
let newBinMarshall
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
packageRepoUtilsMock = new PackageRepoUtils()
|
|
14
|
-
newBinMarshall = new NewBinMarshall({ packageRepoUtils: packageRepoUtilsMock })
|
|
15
|
-
|
|
16
|
-
// Mock context and task for the marshall
|
|
17
|
-
newBinMarshall.init(
|
|
18
|
-
{
|
|
19
|
-
marshalls: {
|
|
20
|
-
newBin: {
|
|
21
|
-
status: null,
|
|
22
|
-
errors: [],
|
|
23
|
-
warnings: [],
|
|
24
|
-
data: {}
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
pkgs: [] // Not directly used by validate but part of ctx
|
|
28
|
-
},
|
|
29
|
-
{ output: '' } // Mock task object
|
|
30
|
-
)
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
afterEach(() => {
|
|
34
|
-
jest.clearAllMocks()
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
const mockPackageInfo = (packageName, versions) => {
|
|
38
|
-
const fullPackageData = {
|
|
39
|
-
name: packageName,
|
|
40
|
-
versions: {},
|
|
41
|
-
'dist-tags': {}
|
|
42
|
-
}
|
|
43
|
-
let latestTag = null
|
|
44
|
-
Object.keys(versions).forEach((vStr) => {
|
|
45
|
-
fullPackageData.versions[vStr] = {
|
|
46
|
-
name: packageName,
|
|
47
|
-
version: vStr,
|
|
48
|
-
bin: versions[vStr].bin
|
|
49
|
-
// other fields like scripts, dependencies might be needed if other marshalls run
|
|
50
|
-
}
|
|
51
|
-
if (!latestTag || require('semver').gt(vStr, latestTag)) {
|
|
52
|
-
latestTag = vStr
|
|
53
|
-
}
|
|
54
|
-
})
|
|
55
|
-
if (latestTag) {
|
|
56
|
-
fullPackageData['dist-tags'].latest = latestTag
|
|
57
|
-
}
|
|
58
|
-
return fullPackageData
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
test('should return correct title', () => {
|
|
62
|
-
const newBinMarshall = new NewBinMarshall({
|
|
63
|
-
packageRepoUtils: null
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
const title = newBinMarshall.title()
|
|
67
|
-
expect(title).toBe('Checking for new binaries introduced in package.json')
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('should pass if no previous version exists', async () => {
|
|
71
|
-
const pkg = {
|
|
72
|
-
packageName: 'test-pkg',
|
|
73
|
-
packageVersion: '1.0.0',
|
|
74
|
-
packageString: 'test-pkg@1.0.0'
|
|
75
|
-
}
|
|
76
|
-
const versions = {
|
|
77
|
-
'1.0.0': { bin: { 'my-cli': 'cli.js' } }
|
|
78
|
-
}
|
|
79
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(mockPackageInfo('test-pkg', versions))
|
|
80
|
-
packageRepoUtilsMock.getSemVer.mockImplementation(async (name, version) => version)
|
|
81
|
-
|
|
82
|
-
await expect(newBinMarshall.validate(pkg)).resolves.toBeUndefined()
|
|
83
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(0)
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
it('should pass if bin field is the same', async () => {
|
|
87
|
-
const pkg = {
|
|
88
|
-
packageName: 'test-pkg',
|
|
89
|
-
packageVersion: '1.0.1',
|
|
90
|
-
packageString: 'test-pkg@1.0.1'
|
|
91
|
-
}
|
|
92
|
-
const versions = {
|
|
93
|
-
'1.0.0': { bin: { 'my-cli': 'cli.js' } },
|
|
94
|
-
'1.0.1': { bin: { 'my-cli': 'cli.js' } }
|
|
95
|
-
}
|
|
96
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(mockPackageInfo('test-pkg', versions))
|
|
97
|
-
packageRepoUtilsMock.getSemVer.mockImplementation(async (name, version) => version)
|
|
98
|
-
|
|
99
|
-
await expect(newBinMarshall.validate(pkg)).resolves.toBeUndefined()
|
|
100
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(0)
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
it('should warn if a new binary is introduced (object format)', async () => {
|
|
104
|
-
const pkg = {
|
|
105
|
-
packageName: 'test-pkg',
|
|
106
|
-
packageVersion: '1.0.1',
|
|
107
|
-
packageString: 'test-pkg@1.0.1'
|
|
108
|
-
}
|
|
109
|
-
const versions = {
|
|
110
|
-
'1.0.0': { bin: { 'old-cli': 'old.js' } },
|
|
111
|
-
'1.0.1': { bin: { 'old-cli': 'old.js', 'new-cli': 'new.js' } }
|
|
112
|
-
}
|
|
113
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(mockPackageInfo('test-pkg', versions))
|
|
114
|
-
packageRepoUtilsMock.getSemVer.mockImplementation(async (name, version) => version)
|
|
115
|
-
|
|
116
|
-
await expect(newBinMarshall.validate(pkg)).rejects.toThrow(
|
|
117
|
-
'New binaries detected for test-pkg@1.0.1'
|
|
118
|
-
)
|
|
119
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(1)
|
|
120
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings[0].message).toContain(
|
|
121
|
-
"introduces a new binary 'new-cli'"
|
|
122
|
-
)
|
|
123
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings[0].message).toContain(
|
|
124
|
-
"compared to version '1.0.0'"
|
|
125
|
-
)
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
it('should warn if a new binary is introduced (string format to object)', async () => {
|
|
129
|
-
const pkg = {
|
|
130
|
-
packageName: 'test-pkg',
|
|
131
|
-
packageVersion: '1.0.1',
|
|
132
|
-
packageString: 'test-pkg@1.0.1'
|
|
133
|
-
}
|
|
134
|
-
// Previous version has string bin, new one has object bin
|
|
135
|
-
const versions = {
|
|
136
|
-
'1.0.0': { bin: 'old.js' }, // This will be normalized to { 'test-pkg': 'old.js' }
|
|
137
|
-
'1.0.1': { bin: { 'test-pkg': 'old.js', 'new-cli': 'new.js' } }
|
|
138
|
-
}
|
|
139
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(mockPackageInfo('test-pkg', versions))
|
|
140
|
-
packageRepoUtilsMock.getSemVer.mockImplementation(async (name, version) => version)
|
|
141
|
-
|
|
142
|
-
await expect(newBinMarshall.validate(pkg)).rejects.toThrow(
|
|
143
|
-
'New binaries detected for test-pkg@1.0.1'
|
|
144
|
-
)
|
|
145
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(1)
|
|
146
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings[0].message).toContain(
|
|
147
|
-
"introduces a new binary 'new-cli'"
|
|
148
|
-
)
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
it('should warn if a new binary is introduced (string format for both, new name)', async () => {
|
|
152
|
-
// This case implies the package name itself changed or the bin refers to a different command name
|
|
153
|
-
// For this marshall, we primarily care about new keys in the (normalized) bin object.
|
|
154
|
-
// If package name changes, it's a different package, not really a "new bin" for the *same* package.
|
|
155
|
-
// However, if `bin` was a string `old-bin-name.js` and becomes `new-bin-name.js`,
|
|
156
|
-
// and assuming the package name (key) remains the same, this is not a *new* binary key.
|
|
157
|
-
// This test will check introduction of a string bin when there was none.
|
|
158
|
-
const pkg = {
|
|
159
|
-
packageName: 'test-pkg',
|
|
160
|
-
packageVersion: '1.0.1',
|
|
161
|
-
packageString: 'test-pkg@1.0.1'
|
|
162
|
-
}
|
|
163
|
-
const versions = {
|
|
164
|
-
'1.0.0': { bin: null }, // No bin before
|
|
165
|
-
'1.0.1': { bin: 'new.js' } // New bin (string)
|
|
166
|
-
}
|
|
167
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(mockPackageInfo('test-pkg', versions))
|
|
168
|
-
packageRepoUtilsMock.getSemVer.mockImplementation(async (name, version) => version)
|
|
169
|
-
|
|
170
|
-
await expect(newBinMarshall.validate(pkg)).rejects.toThrow(
|
|
171
|
-
'New binaries detected for test-pkg@1.0.1'
|
|
172
|
-
)
|
|
173
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(1)
|
|
174
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings[0].message).toContain(
|
|
175
|
-
"introduces a new binary 'test-pkg'"
|
|
176
|
-
)
|
|
177
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings[0].message).toContain("command: 'new.js'")
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
it('should pass if a binary is removed', async () => {
|
|
181
|
-
const pkg = {
|
|
182
|
-
packageName: 'test-pkg',
|
|
183
|
-
packageVersion: '1.0.1',
|
|
184
|
-
packageString: 'test-pkg@1.0.1'
|
|
185
|
-
}
|
|
186
|
-
const versions = {
|
|
187
|
-
'1.0.0': { bin: { 'old-cli': 'old.js', 'to-remove': 'remove.js' } },
|
|
188
|
-
'1.0.1': { bin: { 'old-cli': 'old.js' } }
|
|
189
|
-
}
|
|
190
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(mockPackageInfo('test-pkg', versions))
|
|
191
|
-
packageRepoUtilsMock.getSemVer.mockImplementation(async (name, version) => version)
|
|
192
|
-
|
|
193
|
-
await expect(newBinMarshall.validate(pkg)).resolves.toBeUndefined()
|
|
194
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(0)
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
it('should pass if bin changes from string to object but represents the same binary', async () => {
|
|
198
|
-
const pkg = {
|
|
199
|
-
packageName: 'test-pkg',
|
|
200
|
-
packageVersion: '1.0.1',
|
|
201
|
-
packageString: 'test-pkg@1.0.1'
|
|
202
|
-
}
|
|
203
|
-
const versions = {
|
|
204
|
-
'1.0.0': { bin: 'cli.js' }, // Normalized: { 'test-pkg': 'cli.js' }
|
|
205
|
-
'1.0.1': { bin: { 'test-pkg': 'cli.js' } }
|
|
206
|
-
}
|
|
207
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(mockPackageInfo('test-pkg', versions))
|
|
208
|
-
packageRepoUtilsMock.getSemVer.mockImplementation(async (name, version) => version)
|
|
209
|
-
|
|
210
|
-
await expect(newBinMarshall.validate(pkg)).resolves.toBeUndefined()
|
|
211
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(0)
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
it('should pass if bin changes from object to string but represents the same binary', async () => {
|
|
215
|
-
const pkg = {
|
|
216
|
-
packageName: 'test-pkg',
|
|
217
|
-
packageVersion: '1.0.1',
|
|
218
|
-
packageString: 'test-pkg@1.0.1'
|
|
219
|
-
}
|
|
220
|
-
const versions = {
|
|
221
|
-
'1.0.0': { bin: { 'test-pkg': 'cli.js' } },
|
|
222
|
-
'1.0.1': { bin: 'cli.js' } // Normalized: { 'test-pkg': 'cli.js' }
|
|
223
|
-
}
|
|
224
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(mockPackageInfo('test-pkg', versions))
|
|
225
|
-
packageRepoUtilsMock.getSemVer.mockImplementation(async (name, version) => version)
|
|
226
|
-
|
|
227
|
-
await expect(newBinMarshall.validate(pkg)).resolves.toBeUndefined()
|
|
228
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(0)
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
it('should handle package version being a dist-tag like "latest"', async () => {
|
|
232
|
-
const pkg = {
|
|
233
|
-
packageName: 'test-pkg',
|
|
234
|
-
packageVersion: 'latest',
|
|
235
|
-
packageString: 'test-pkg@latest'
|
|
236
|
-
}
|
|
237
|
-
const versions = {
|
|
238
|
-
'1.0.0': { bin: { 'my-cli': 'cli.js' } },
|
|
239
|
-
'1.0.1': { bin: { 'my-cli': 'cli.js', 'new-cli': 'new.js' } } // latest points to 1.0.1
|
|
240
|
-
}
|
|
241
|
-
const mockPkgInfo = mockPackageInfo('test-pkg', versions)
|
|
242
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(mockPkgInfo)
|
|
243
|
-
// Mock getSemVer to resolve 'latest' to '1.0.1'
|
|
244
|
-
packageRepoUtilsMock.getSemVer.mockImplementation(async (name, version) => {
|
|
245
|
-
if (version === 'latest') return '1.0.1'
|
|
246
|
-
return version
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
await expect(newBinMarshall.validate(pkg)).rejects.toThrow(
|
|
250
|
-
'New binaries detected for test-pkg@latest'
|
|
251
|
-
)
|
|
252
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(1)
|
|
253
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings[0].message).toContain(
|
|
254
|
-
"introduces a new binary 'new-cli'"
|
|
255
|
-
)
|
|
256
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings[0].message).toContain(
|
|
257
|
-
"compared to version '1.0.0'"
|
|
258
|
-
)
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
it('should pass if package info cannot be fetched', async () => {
|
|
262
|
-
const pkg = {
|
|
263
|
-
packageName: 'test-pkg',
|
|
264
|
-
packageVersion: '1.0.0',
|
|
265
|
-
packageString: 'test-pkg@1.0.0'
|
|
266
|
-
}
|
|
267
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(null) // Simulate failed fetch
|
|
268
|
-
packageRepoUtilsMock.getSemVer.mockImplementation(async (name, version) => version)
|
|
269
|
-
|
|
270
|
-
await expect(newBinMarshall.validate(pkg)).resolves.toBeUndefined()
|
|
271
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(0)
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
it('should pass if target version string cannot be resolved', async () => {
|
|
275
|
-
const pkg = {
|
|
276
|
-
packageName: 'test-pkg',
|
|
277
|
-
packageVersion: 'nonexistent-tag',
|
|
278
|
-
packageString: 'test-pkg@nonexistent-tag'
|
|
279
|
-
}
|
|
280
|
-
const versions = { '1.0.0': { bin: 'cli.js' } }
|
|
281
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(mockPackageInfo('test-pkg', versions))
|
|
282
|
-
packageRepoUtilsMock.getSemVer.mockResolvedValue(null) // Simulate tag not resolving
|
|
283
|
-
|
|
284
|
-
await expect(newBinMarshall.validate(pkg)).resolves.toBeUndefined()
|
|
285
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(0)
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
it('should handle multiple new binaries being introduced', async () => {
|
|
289
|
-
const pkg = {
|
|
290
|
-
packageName: 'test-pkg',
|
|
291
|
-
packageVersion: '1.0.1',
|
|
292
|
-
packageString: 'test-pkg@1.0.1'
|
|
293
|
-
}
|
|
294
|
-
const versions = {
|
|
295
|
-
'1.0.0': { bin: { 'old-cli': 'old.js' } },
|
|
296
|
-
'1.0.1': { bin: { 'old-cli': 'old.js', 'new-cli-1': 'new1.js', 'new-cli-2': 'new2.js' } }
|
|
297
|
-
}
|
|
298
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(mockPackageInfo('test-pkg', versions))
|
|
299
|
-
packageRepoUtilsMock.getSemVer.mockImplementation(async (name, version) => version)
|
|
300
|
-
|
|
301
|
-
await expect(newBinMarshall.validate(pkg)).rejects.toThrow(
|
|
302
|
-
'New binaries detected for test-pkg@1.0.1'
|
|
303
|
-
)
|
|
304
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(2)
|
|
305
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings[0].message).toContain(
|
|
306
|
-
"introduces a new binary 'new-cli-1'"
|
|
307
|
-
)
|
|
308
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings[1].message).toContain(
|
|
309
|
-
"introduces a new binary 'new-cli-2'"
|
|
310
|
-
)
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
it('should pass if the new version has no bin field but old one did', async () => {
|
|
314
|
-
const pkg = {
|
|
315
|
-
packageName: 'test-pkg',
|
|
316
|
-
packageVersion: '1.0.1',
|
|
317
|
-
packageString: 'test-pkg@1.0.1'
|
|
318
|
-
}
|
|
319
|
-
const versions = {
|
|
320
|
-
'1.0.0': { bin: { 'old-cli': 'old.js' } },
|
|
321
|
-
'1.0.1': { bin: null } // No bin in new version
|
|
322
|
-
}
|
|
323
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(mockPackageInfo('test-pkg', versions))
|
|
324
|
-
packageRepoUtilsMock.getSemVer.mockImplementation(async (name, version) => version)
|
|
325
|
-
|
|
326
|
-
await expect(newBinMarshall.validate(pkg)).resolves.toBeUndefined()
|
|
327
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(0)
|
|
328
|
-
})
|
|
329
|
-
|
|
330
|
-
it('should correctly use package name from version data for string bin normalization', async () => {
|
|
331
|
-
// Scenario: pkg.packageName might be different from the actual name in package.json (e.g. alias)
|
|
332
|
-
// The marshall should use the name from the fetched package.json for normalization.
|
|
333
|
-
const pkg = {
|
|
334
|
-
packageName: 'alias-pkg',
|
|
335
|
-
packageVersion: '1.0.1',
|
|
336
|
-
packageString: 'alias-pkg@1.0.1'
|
|
337
|
-
}
|
|
338
|
-
const actualPackageName = 'actual-pkg-name'
|
|
339
|
-
|
|
340
|
-
// Mock getPackageInfo to return versions with the actual package name
|
|
341
|
-
const versions = {
|
|
342
|
-
'1.0.0': { bin: null }, // No bin in old version
|
|
343
|
-
'1.0.1': { bin: 'cli.js' } // Bin is a string, should be keyed by actualPackageName
|
|
344
|
-
}
|
|
345
|
-
const mockPkgData = mockPackageInfo(actualPackageName, versions) // mockPackageInfo uses the name for versions too
|
|
346
|
-
|
|
347
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(mockPkgData)
|
|
348
|
-
packageRepoUtilsMock.getSemVer.mockImplementation(async (name, version) => version)
|
|
349
|
-
|
|
350
|
-
await expect(newBinMarshall.validate(pkg)).rejects.toThrow(
|
|
351
|
-
`New binaries detected for ${pkg.packageString}`
|
|
352
|
-
)
|
|
353
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(1)
|
|
354
|
-
// Check that the binary name used in the warning is the actualPackageName
|
|
355
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings[0].message).toContain(
|
|
356
|
-
`introduces a new binary '${actualPackageName}'`
|
|
357
|
-
)
|
|
358
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings[0].message).toContain(`command: 'cli.js'`)
|
|
359
|
-
})
|
|
360
|
-
|
|
361
|
-
it('should use pkg.packageName when newVersionData.name is falsy (line 40 coverage)', async () => {
|
|
362
|
-
const pkg = {
|
|
363
|
-
packageName: 'fallback-pkg',
|
|
364
|
-
packageVersion: '1.0.1',
|
|
365
|
-
packageString: 'fallback-pkg@1.0.1'
|
|
366
|
-
}
|
|
367
|
-
const versions = {
|
|
368
|
-
'1.0.0': { bin: null },
|
|
369
|
-
'1.0.1': { bin: 'cli.js' } // This version will have name: falsy
|
|
370
|
-
}
|
|
371
|
-
const mockPkgData = mockPackageInfo('fallback-pkg', versions)
|
|
372
|
-
// Set name to falsy to test the fallback in line 40: newVersionData.name || pkg.packageName
|
|
373
|
-
mockPkgData.versions['1.0.1'].name = ''
|
|
374
|
-
|
|
375
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(mockPkgData)
|
|
376
|
-
packageRepoUtilsMock.getSemVer.mockImplementation(async (name, version) => version)
|
|
377
|
-
|
|
378
|
-
await expect(newBinMarshall.validate(pkg)).rejects.toThrow(
|
|
379
|
-
'New binaries detected for fallback-pkg@1.0.1'
|
|
380
|
-
)
|
|
381
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(1)
|
|
382
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings[0].message).toContain(
|
|
383
|
-
"introduces a new binary 'fallback-pkg'"
|
|
384
|
-
)
|
|
385
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings[0].message).toContain("command: 'cli.js'")
|
|
386
|
-
})
|
|
387
|
-
|
|
388
|
-
it('should use pkg.packageName when previousVersionData.name is falsy (line 56 coverage)', async () => {
|
|
389
|
-
const pkg = {
|
|
390
|
-
packageName: 'fallback-pkg',
|
|
391
|
-
packageVersion: '1.0.1',
|
|
392
|
-
packageString: 'fallback-pkg@1.0.1'
|
|
393
|
-
}
|
|
394
|
-
const versions = {
|
|
395
|
-
'1.0.0': { bin: 'old.js' }, // This version will have name: falsy
|
|
396
|
-
'1.0.1': { bin: { 'fallback-pkg': 'old.js', 'new-cli': 'new.js' } }
|
|
397
|
-
}
|
|
398
|
-
const mockPkgData = mockPackageInfo('fallback-pkg', versions)
|
|
399
|
-
// Set name to falsy to test the fallback in line 56: previousVersionData.name || pkg.packageName
|
|
400
|
-
mockPkgData.versions['1.0.0'].name = undefined
|
|
401
|
-
|
|
402
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(mockPkgData)
|
|
403
|
-
packageRepoUtilsMock.getSemVer.mockImplementation(async (name, version) => version)
|
|
404
|
-
|
|
405
|
-
await expect(newBinMarshall.validate(pkg)).rejects.toThrow(
|
|
406
|
-
'New binaries detected for fallback-pkg@1.0.1'
|
|
407
|
-
)
|
|
408
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(1)
|
|
409
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings[0].message).toContain(
|
|
410
|
-
"introduces a new binary 'new-cli'"
|
|
411
|
-
)
|
|
412
|
-
})
|
|
413
|
-
|
|
414
|
-
it('should use pkg.packageName when both version data names are falsy (covers both lines)', async () => {
|
|
415
|
-
const pkg = {
|
|
416
|
-
packageName: 'double-fallback-pkg',
|
|
417
|
-
packageVersion: '1.0.1',
|
|
418
|
-
packageString: 'double-fallback-pkg@1.0.1'
|
|
419
|
-
}
|
|
420
|
-
const versions = {
|
|
421
|
-
'1.0.0': { bin: null },
|
|
422
|
-
'1.0.1': { bin: 'cli.js' }
|
|
423
|
-
}
|
|
424
|
-
const mockPkgData = mockPackageInfo('double-fallback-pkg', versions)
|
|
425
|
-
// Set both names to falsy values to test both fallbacks
|
|
426
|
-
mockPkgData.versions['1.0.0'].name = null
|
|
427
|
-
mockPkgData.versions['1.0.1'].name = ''
|
|
428
|
-
|
|
429
|
-
packageRepoUtilsMock.getPackageInfo.mockResolvedValue(mockPkgData)
|
|
430
|
-
packageRepoUtilsMock.getSemVer.mockImplementation(async (name, version) => version)
|
|
431
|
-
|
|
432
|
-
await expect(newBinMarshall.validate(pkg)).rejects.toThrow(
|
|
433
|
-
'New binaries detected for double-fallback-pkg@1.0.1'
|
|
434
|
-
)
|
|
435
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings).toHaveLength(1)
|
|
436
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings[0].message).toContain(
|
|
437
|
-
"introduces a new binary 'double-fallback-pkg'"
|
|
438
|
-
)
|
|
439
|
-
expect(newBinMarshall.ctx.marshalls.newBin.warnings[0].message).toContain("command: 'cli.js'")
|
|
440
|
-
})
|
|
441
|
-
})
|