Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ coverage/
junit.xml
package-lock.json
.claude
.cursor/
8 changes: 4 additions & 4 deletions bin/run → bin/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ OF ANY KIND, either express or implied. See the License for the specific languag
governing permissions and limitations under the License.
*/

const oclif = require('@oclif/core')
import { run, handle, flush } from '@oclif/core'

oclif.run()
.then(require('@oclif/core/flush'))
.catch(require('@oclif/core/handle'))
await run()
.then(flush)
.catch(handle)
16 changes: 12 additions & 4 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,20 @@ OF ANY KIND, either express or implied. See the License for the specific languag
governing permissions and limitations under the License.
*/

const aioLibConfig = require('@adobe/eslint-config-aio-lib-config')
const pluginJest = require('eslint-plugin-jest')
import aioLibConfig from '@adobe/eslint-config-aio-lib-config'
import vitest from '@vitest/eslint-plugin'

module.exports = [
export default [
...aioLibConfig,
pluginJest.configs['flat/recommended'],
{
files: ['test/**'],
...vitest.configs.recommended,
languageOptions: {
globals: {
...vitest.environments.env.globals
}
}
},
{
rules: {
'jsdoc/no-defaults': 'off'
Expand Down
25 changes: 7 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,23 @@
"description": "Generate and validate private certificates, and public key pairs for use with Adobe IO Console",
"repository": "adobe/aio-cli-plugin-certificate",
"homepage": "https://github.com/adobe/aio-cli-plugin-certificate",
"type": "module",
"dependencies": {
"@oclif/core": "^4.9.0",
"debug": "^4.3.3",
"fs-extra": "^9.0.0",
"fs-extra": "^11.3.5",
"node-forge": "^1.3.0"
},
"devDependencies": {
"@adobe/eslint-config-aio-lib-config": "^5.0.0",
"eslint": "^9",
"eslint-plugin-jest": "^29",
"@vitest/coverage-v8": "^4.1.8",
"@vitest/eslint-plugin": "^1",
"eslint-plugin-jsdoc": "^48",
"jest": "^29",
"neostandard": "^0",
"oclif": "^4.0.0",
"stdout-stderr": "^0.1.9"
"stdout-stderr": "^0.1.9",
"vitest": "^4.1.8"
},
"engines": {
"node": ">=20"
Expand All @@ -43,22 +45,9 @@
"scripts": {
"posttest": "eslint src test",
"test": "npm run unit-tests",
"unit-tests": "jest --ci",
"unit-tests": "vitest run",
"prepack": "oclif manifest && oclif readme --no-aliases",
"postpack": "rm -f oclif.manifest.json",
"version": "oclif readme && git add README.md"
},
"jest": {
"collectCoverage": true,
"testPathIgnorePatterns": [
"<rootDir>/tests/fixtures/"
],
"coveragePathIgnorePatterns": [
"<rootDir>/tests/fixtures/"
],
"testEnvironment": "node",
"setupFilesAfterEnv": [
"./test/jest.setup.js"
]
}
}
14 changes: 5 additions & 9 deletions src/certificate.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ OF ANY KIND, either express or implied. See the License for the specific languag
governing permissions and limitations under the License.
*/

const debug = require('debug')('aio-cli-plugin-certificate:helpers')
const forge = require('node-forge')
const pki = forge.pki
const asn1 = forge.asn1
import logDebug from 'debug'
import forge from 'node-forge'
const { pki, asn1 } = forge
const debug = logDebug('aio-cli-plugin-certificate:helpers')

/**
* Computes the SHA-1 digest of the entire DER-encoded x.509 certificate
Expand Down Expand Up @@ -164,8 +164,4 @@ function verify (pemCert) {
}
}

module.exports = {
fingerprint,
generate,
verify
}
export { fingerprint, generate, verify }
11 changes: 6 additions & 5 deletions src/commands/certificate/fingerprint.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ OF ANY KIND, either express or implied. See the License for the specific languag
governing permissions and limitations under the License.
*/

const { Command, Args } = require('@oclif/core')
const fs = require('fs-extra')
const debug = require('debug')('aio-cli-plugin-certificate:fingerprint')
import { Command, Args } from '@oclif/core'
import fs from 'fs-extra'
import logDebug from 'debug'
import * as cert from '../../certificate.js'

const cert = require('../../certificate')
const debug = logDebug('aio-cli-plugin-certificate:fingerprint')

class FingerprintCommand extends Command {
async run () {
Expand Down Expand Up @@ -47,4 +48,4 @@ FingerprintCommand.args = {
})
}

module.exports = FingerprintCommand
export default FingerprintCommand
11 changes: 6 additions & 5 deletions src/commands/certificate/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ OF ANY KIND, either express or implied. See the License for the specific languag
governing permissions and limitations under the License.
*/

const { Command, Flags } = require('@oclif/core')
const fs = require('fs-extra')
const debug = require('debug')('aio-cli-plugin-certificate:generate')
import { Command, Flags } from '@oclif/core'
import fs from 'fs-extra'
Comment thread
hannah-taub-1 marked this conversation as resolved.
import logDebug from 'debug'
Comment thread
hannah-taub-1 marked this conversation as resolved.
import * as cert from '../../certificate.js'

const cert = require('../../certificate')
const debug = logDebug('aio-cli-plugin-certificate:generate')

class GenerateCommand extends Command {
async run () {
Expand Down Expand Up @@ -79,4 +80,4 @@ GenerateCommand.flags = {
})
}

module.exports = GenerateCommand
export default GenerateCommand
4 changes: 2 additions & 2 deletions src/commands/certificate/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ OF ANY KIND, either express or implied. See the License for the specific languag
governing permissions and limitations under the License.
*/

const { Command, Help } = require('@oclif/core')
import { Command, Help } from '@oclif/core'

class AIOCommand extends Command {
async run () {
Expand All @@ -21,4 +21,4 @@ class AIOCommand extends Command {

AIOCommand.description = 'Generate, fingerprint, or verify a certificate for use with Adobe I/O'

module.exports = AIOCommand
export default AIOCommand
11 changes: 6 additions & 5 deletions src/commands/certificate/verify.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ OF ANY KIND, either express or implied. See the License for the specific languag
governing permissions and limitations under the License.
*/

const { Command, Flags, Args } = require('@oclif/core')
const fs = require('fs-extra')
const debug = require('debug')('aio-cli-plugin-certificate:verify')
import { Command, Flags, Args } from '@oclif/core'
import fs from 'fs-extra'
Comment thread
hannah-taub-1 marked this conversation as resolved.
import logDebug from 'debug'
import * as cert from '../../certificate.js'

const cert = require('../../certificate')
const debug = logDebug('aio-cli-plugin-certificate:verify')

class VerifyCommand extends Command {
async run () {
Expand Down Expand Up @@ -77,4 +78,4 @@ VerifyCommand.args = {
})
}

module.exports = VerifyCommand
export default VerifyCommand
85 changes: 55 additions & 30 deletions test/commands/certificate/fingerprint.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ governing permissions and limitations under the License.
*/

// const { stdout } = require('stdout-stderr')
Comment thread
hannah-taub-1 marked this conversation as resolved.
const commandPath = '../../../src/commands/certificate/fingerprint'
let TheCommand
jest.isolateModules(() => {
TheCommand = require(commandPath)
})
import { vi } from 'vitest'
import mockFS from 'fs-extra'
Comment thread
hannah-taub-1 marked this conversation as resolved.

// we don't import the command up here to allow for some tests to run with isolated mocks
// since vitest doesn't support isolateModules
const commandPath = '../../../src/commands/certificate/fingerprint.js'
const validCertPem = `
-----BEGIN CERTIFICATE-----
MIIDMTCCAhmgAwIBAgIHAWVCcDJVYDANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQD
Expand All @@ -40,36 +40,60 @@ B9+DCYg=
`
const validCertFingerprint = '38f65e26bd3869ec3ca029cc0b3df98de29172b9'

test('exports', async () => {
expect(typeof TheCommand).toEqual('function')
})
describe('basic functionality', () => {
let TheCommand

test('description', async () => {
expect(TheCommand.description).toBeDefined()
})
beforeAll(async () => {
vi.resetModules()
TheCommand = (await import(commandPath)).default
})

test('exports', async () => {
expect(typeof TheCommand).toEqual('function')
})

test('description', async () => {
expect(TheCommand.description).toBeDefined()
})

test('args', async () => {
expect(Object.keys(TheCommand.args)[0]).toBeDefined()
test('args', async () => {
expect(Object.keys(TheCommand.args)[0]).toBeDefined()
})
})

const mockConfig = { runHook: jest.fn().mockResolvedValue({ successes: [], failures: [] }) }
const mockConfig = { runHook: vi.fn().mockResolvedValue({ successes: [], failures: [] }) }

describe('instance methods - mock forge', () => {
let CommandUnderTest, command, handleError, mockFS, mockForge
jest.isolateModules(() => {
CommandUnderTest = require(commandPath)
mockFS = require('fs-extra')
mockForge = require('node-forge')
jest.mock('node-forge')
let CommandUnderTest, command, handleError, mockForge

beforeAll(async () => {
vi.resetModules()
// vitest does not support isolateModules, so we have to build our own sandbox mock here
vi.doMock('node-forge', async (importOriginal) => {
const mod = await importOriginal()
const forge = mod.default ?? mod
return {
...mod,
default: {
...forge,
pki: {
...forge.pki,
certificateFromPem: vi.fn()
}
}
}
})
CommandUnderTest = (await import(commandPath)).default
mockForge = (await import('node-forge')).default
})

beforeEach(() => {
command = new CommandUnderTest([], mockConfig)
handleError = jest.spyOn(command, 'error')
handleError = vi.spyOn(command, 'error')
})

afterEach(() => {
jest.clearAllMocks()
vi.clearAllMocks()
})

test('run missing args', async () => {
Expand All @@ -96,27 +120,28 @@ describe('instance methods - mock forge', () => {
})

describe('instance methods - real forge', () => {
let CommandUnderTest, command, handleError, mockFS
jest.isolateModules(() => {
mockFS = require('fs-extra')
jest.unmock('node-forge')
CommandUnderTest = require(commandPath)
let CommandUnderTest, command, handleError

beforeAll(async () => {
vi.resetModules()
vi.doUnmock('node-forge')
CommandUnderTest = (await import(commandPath)).default
})

beforeEach(() => {
command = new CommandUnderTest([], mockConfig)
handleError = jest.spyOn(command, 'error')
handleError = vi.spyOn(command, 'error')
})

afterEach(() => {
jest.clearAllMocks()
vi.clearAllMocks()
})

test('run with valid cert pem', async () => {
mockFS.existsSync.mockReturnValue(true)
mockFS.readFileSync.mockReturnValue(Buffer.from(validCertPem))
command.argv = ['file']
const logSpy = jest.spyOn(command, 'log')
const logSpy = vi.spyOn(command, 'log')
await expect(command.run()).resolves.toBeUndefined()
expect(logSpy).toHaveBeenCalledWith(validCertFingerprint)
expect(handleError).not.toHaveBeenCalled()
Expand Down
14 changes: 7 additions & 7 deletions test/commands/certificate/generate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ governing permissions and limitations under the License.
*/

// const { stdout } = require('stdout-stderr')
const TheCommand = require('../../../src/commands/certificate/generate')

const mockFS = require('fs-extra')
const forge = require('node-forge')
import { vi } from 'vitest'
import TheCommand from '../../../src/commands/certificate/generate.js'
import mockFS from 'fs-extra'
import forge from 'node-forge'

test('exports', async () => {
expect(typeof TheCommand).toEqual('function')
Expand All @@ -23,18 +23,18 @@ test('description', async () => {
expect(TheCommand.description).toBeDefined()
})

const mockConfig = { runHook: jest.fn().mockResolvedValue({ successes: [], failures: [] }) }
const mockConfig = { runHook: vi.fn().mockResolvedValue({ successes: [], failures: [] }) }

describe('instance methods', () => {
let command, handleError

beforeEach(() => {
command = new TheCommand([], mockConfig)
handleError = jest.spyOn(command, 'error')
handleError = vi.spyOn(command, 'error')
})

afterEach(() => {
jest.clearAllMocks()
vi.clearAllMocks()
})

test('run -- no flags', async () => {
Expand Down
Loading
Loading