Initial Commit
Some checks failed
Publish to Private NPM Registry / publish (push) Failing after 1m0s
Some checks failed
Publish to Private NPM Registry / publish (push) Failing after 1m0s
This commit is contained in:
commit
18cc482293
12 changed files with 4353 additions and 0 deletions
109
.forgejo/workflows/publish.yml
Normal file
109
.forgejo/workflows/publish.yml
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
name: Publish to Private NPM Registry
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: default
|
||||||
|
|
||||||
|
steps:
|
||||||
|
# Checkout the repository
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
# Set up NPM Auth Token
|
||||||
|
- name: Set up NPM Auth Token
|
||||||
|
run: echo "NODE_AUTH_TOKEN=${{ secrets.NPM_TOKEN }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# Set up Node.js
|
||||||
|
- name: Set up Node.js version
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
# Taken from [Repo README](https://github.com/actions/setup-node#readme)
|
||||||
|
#
|
||||||
|
# > Version Spec of the version to use in SemVer notation.
|
||||||
|
# > It also admits such aliases as lts/*, latest, nightly and canary builds
|
||||||
|
# > Examples: 12.x, 10.15.1, >=10.15.0, lts/Hydrogen, 16-nightly, latest, node
|
||||||
|
node-version: '20.x'
|
||||||
|
|
||||||
|
# Taken from [Repo README](https://github.com/actions/setup-node#readme)
|
||||||
|
#
|
||||||
|
# > Optional registry to set up for auth. Will set the registry in a project level .npmrc and .yarnrc file,
|
||||||
|
# > and set up auth to read in from env.NODE_AUTH_TOKEN.
|
||||||
|
# > Default: ''
|
||||||
|
registry-url: 'https://npm.pkg.bridgemanaccessible.ca'
|
||||||
|
|
||||||
|
# Taken from [Repo README](https://github.com/actions/setup-node#readme)
|
||||||
|
#
|
||||||
|
# > Optional scope for authenticating against scoped registries.
|
||||||
|
# > Will fall back to the repository owner when using the GitHub Packages registry (https://npm.pkg.github.com/).
|
||||||
|
scope: '@BridgemanAccessible'
|
||||||
|
|
||||||
|
# Transpile/Build the package (TypeScript -> JavaScript)
|
||||||
|
- name: Transpile/Build the package (TypeScript -> JavaScript)
|
||||||
|
run: |
|
||||||
|
# Because Yarn is used locally better to install and use it than have to debug weird inconsistencies
|
||||||
|
npm install --global yarn
|
||||||
|
|
||||||
|
# Install needed dependencies
|
||||||
|
yarn install
|
||||||
|
|
||||||
|
# Run tests to ensure the package is working as expected before publishing
|
||||||
|
yarn test
|
||||||
|
|
||||||
|
# Build the package
|
||||||
|
yarn build
|
||||||
|
|
||||||
|
- name: Determine Version and Increment (if needed)
|
||||||
|
id: version_check
|
||||||
|
run: |
|
||||||
|
VERSION=$(node -p "require('./package.json').version")
|
||||||
|
echo "Version: $VERSION"
|
||||||
|
|
||||||
|
NAME=$(node -p "require('./package.json').name")
|
||||||
|
LATEST_VERSION=$(npm show $NAME version --registry https://npm.pkg.bridgemanaccessible.ca)
|
||||||
|
echo "Latest version: $LATEST_VERSION"
|
||||||
|
|
||||||
|
if [ "$LATEST_VERSION" != "$VERSION" ]; then
|
||||||
|
echo "Manually updated version detected: $VERSION"
|
||||||
|
else
|
||||||
|
NEW_VERSION=$(npm version patch --no-git-tag-version)
|
||||||
|
echo "New version: $NEW_VERSION"
|
||||||
|
echo "new_version=$NEW_VERSION" >> $GITHUB_ENV
|
||||||
|
echo "version_changed=true" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Commit Version Change (if needed)
|
||||||
|
if: steps.version_check.outputs.version_changed == 'true'
|
||||||
|
run: |
|
||||||
|
# Update remote URL to use the GITHUB_TOKEN for authentication
|
||||||
|
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@git.bridgemanaccessible.ca/${{ github.repository }}.git
|
||||||
|
|
||||||
|
# Setup git user details for committing the version change
|
||||||
|
git config user.name "Forgejo Actions"
|
||||||
|
git config user.email "actions@git.bridgemanaccessible.ca"
|
||||||
|
|
||||||
|
# Commit the version change to the `package.json` file
|
||||||
|
git add package.json
|
||||||
|
git commit -m "[Forgejo Actions] Update version to ${{ env.new_version }}"
|
||||||
|
|
||||||
|
# Push the changes to the repository
|
||||||
|
git push origin HEAD:main
|
||||||
|
|
||||||
|
# Publish to private NPM registry
|
||||||
|
- name: Publish the package
|
||||||
|
run: |
|
||||||
|
# Copy over the files to the build output (`dist`) folder
|
||||||
|
cp package.json dist/package.json
|
||||||
|
cp README.md dist/README.md
|
||||||
|
cp LICENSE dist/LICENSE
|
||||||
|
|
||||||
|
# Change directory to the build output (`dist`) folder
|
||||||
|
cd dist
|
||||||
|
|
||||||
|
# Publish the package to the private NPM registry
|
||||||
|
npm publish --registry http://npm.pkg.bridgemanaccessible.ca/
|
||||||
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Node modules
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Yarn stuff
|
||||||
|
.yarn/
|
||||||
|
.yarnrc.yml
|
||||||
|
|
||||||
|
# Credentials/Secrets
|
||||||
|
.npmrc
|
||||||
|
|
||||||
|
# Build output
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Automation Scripts
|
||||||
|
unpublish-publish.ps1
|
||||||
|
unpublish-publish.sh
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Bridgeman Accessible
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
2
README.md
Normal file
2
README.md
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
# File based Implementation of bridgeman Accessible Auth Keystore
|
||||||
|
This is the file based implementation of the Bridgeman Accessible Auth Keystore.
|
||||||
36
jest.config.cjs
Normal file
36
jest.config.cjs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
module.exports = {
|
||||||
|
preset: 'ts-jest/presets/default-esm',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
|
||||||
|
// Tell Jest where to look for source code and tests
|
||||||
|
roots: ['<rootDir>/src', '<rootDir>/tests'],
|
||||||
|
|
||||||
|
// Explicitly match files in the tests folder
|
||||||
|
testMatch: [
|
||||||
|
'<rootDir>/tests/**/*.test.ts',
|
||||||
|
'<rootDir>/tests/**/*.spec.ts'
|
||||||
|
],
|
||||||
|
|
||||||
|
// Tell Jest to treat .ts files as ESM modules
|
||||||
|
extensionsToTreatAsEsm: ['.ts'],
|
||||||
|
|
||||||
|
// Fixes the "Cannot find module" error
|
||||||
|
moduleNameMapper: {
|
||||||
|
// If the import starts with . or .. and ends with .js, strip the .js
|
||||||
|
'^(\\.{1,2}/.*)\\.js$': '$1',
|
||||||
|
|
||||||
|
// Map the library to mock file
|
||||||
|
// This tells Jest to load simple JS file instead of parsing the complex node_module
|
||||||
|
//'^@BridgemanAccessible/ba-auth$': '<rootDir>/tests/__mocks__/ba-auth.js'
|
||||||
|
},
|
||||||
|
|
||||||
|
// Configure the transformer to enable ESM support
|
||||||
|
transform: {
|
||||||
|
'^.+\\.tsx?$': [
|
||||||
|
'ts-jest',
|
||||||
|
{
|
||||||
|
useESM: true,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
36
package.json
Normal file
36
package.json
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"name": "@BridgemanAccessible/ba-auth_keystore_file",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "A file based Keystore implementation for use in combination with the Bridgeman Accessible Auth Package",
|
||||||
|
"main": "index.js",
|
||||||
|
"types": "index.d.ts",
|
||||||
|
"repository": "https://git.bridgemanaccessible.ca/Bridgeman-Accessible/ba-auth_keystore_file.git",
|
||||||
|
"author": "Bridgeman Accessible<info@bridgemanaccessible.ca>",
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"default": "./index.js",
|
||||||
|
"types": "./index.d.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc -p tsconfig.build.json",
|
||||||
|
"test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jest": "^30.0.0",
|
||||||
|
"@types/node": "^25.2.3",
|
||||||
|
"@types/node-jose": "^1.1.13",
|
||||||
|
"cross-env": "^10.1.0",
|
||||||
|
"jest": "^30.2.0",
|
||||||
|
"ts-jest": "^29.4.6",
|
||||||
|
"typescript": "^5.9.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@BridgemanAccessible/ba-auth": "^1.0.30",
|
||||||
|
"@BridgemanAccessible/ba-logging": "^1.0.1",
|
||||||
|
"node-jose": "^2.2.0"
|
||||||
|
},
|
||||||
|
"packageManager": "yarn@1.22.22"
|
||||||
|
}
|
||||||
89
src/FileKeystore.ts
Normal file
89
src/FileKeystore.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import NodeJose from 'node-jose';
|
||||||
|
import type { JWK as JWKTypes } from 'node-jose';
|
||||||
|
|
||||||
|
import { BaseKeystore, Keystore } from '@BridgemanAccessible/ba-auth/keystore';
|
||||||
|
|
||||||
|
const { JWK } = NodeJose;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File implementation for the keystore
|
||||||
|
*/
|
||||||
|
export class FileKeystore extends BaseKeystore {
|
||||||
|
private keystoreFilePath: string | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new keystore from a file
|
||||||
|
*
|
||||||
|
* @param keystoreFile The name of the keystore file
|
||||||
|
* @param certDir The directory that contains the keystore file
|
||||||
|
*/
|
||||||
|
private constructor() {
|
||||||
|
// Call the super constructor
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the file path of the keystore */
|
||||||
|
getKeystoreFilePath(): string {
|
||||||
|
if(typeof this.keystoreFilePath === 'undefined') {
|
||||||
|
throw new Error('Keystore file path not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.keystoreFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup/Load the keystore
|
||||||
|
*
|
||||||
|
* If the keystore file doesn't exist, it will be created (including generating an initial signing and encryption key pair)
|
||||||
|
* If the keystore file does exist, it will be loaded
|
||||||
|
*
|
||||||
|
* @param keystoreFile The name of the keystore file
|
||||||
|
* @param certDir The directory to save the keystore file in
|
||||||
|
* @returns The keystore object
|
||||||
|
*/
|
||||||
|
private async setupKeystore(keystoreFile: string, certDir: string): Promise<JWKTypes.KeyStore> {
|
||||||
|
// Combine the directory and the file into a singular file path
|
||||||
|
this.keystoreFilePath = path.join(certDir, keystoreFile);
|
||||||
|
|
||||||
|
// Create a variable to hold the keystore
|
||||||
|
let keystore: JWKTypes.KeyStore;
|
||||||
|
|
||||||
|
// Check if the keystore file already exists
|
||||||
|
if (!fs.existsSync(this.keystoreFilePath)) {
|
||||||
|
// Because the keystore file doesn't exist, check if the cert directory exists
|
||||||
|
if (!fs.existsSync(certDir)) {
|
||||||
|
// Because the directory doesn't exist, create it
|
||||||
|
fs.mkdirSync(certDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new keystore
|
||||||
|
keystore = JWK.createKeyStore();
|
||||||
|
|
||||||
|
// Generate two new RSA key pairs (one for signing the other foe encryption)
|
||||||
|
const signingKey = await keystore.generate('RSA', 2048, { alg: 'RS256', use: 'sig' });
|
||||||
|
|
||||||
|
//const encryptionKey = await keystore.generate('RSA', 2048, { enc: 'A128CBC-HS256', use: 'enc' });
|
||||||
|
|
||||||
|
const encryptionKey = await keystore.generate('RSA', 2048, { /*alg: 'RS-OAEP', enc: 'A128CBC-HS256',*/ use: 'enc' });
|
||||||
|
|
||||||
|
// Save the keystore to a file
|
||||||
|
fs.writeFileSync(this.keystoreFilePath, JSON.stringify(keystore.toJSON(true), null, 4));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Load the keystore from the file
|
||||||
|
keystore = await JWK.asKeyStore(JSON.parse(fs.readFileSync(this.keystoreFilePath, 'utf-8')));
|
||||||
|
}
|
||||||
|
|
||||||
|
return keystore;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async create(keystoreFile: string = 'keystore.json', certDir: string = '.cert') {
|
||||||
|
const keystore = new FileKeystore();
|
||||||
|
keystore.keystore = await keystore.setupKeystore(keystoreFile, certDir);
|
||||||
|
return keystore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keystore.addKeystoreType('file', FileKeystore.create);
|
||||||
3
src/index.ts
Normal file
3
src/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { FileKeystore } from './FileKeystore.js';
|
||||||
|
|
||||||
|
export { FileKeystore };
|
||||||
147
tests/FileKeystore.test.ts
Normal file
147
tests/FileKeystore.test.ts
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
import path from 'path';
|
||||||
|
import NodeJose from 'node-jose';
|
||||||
|
import { jest, describe, test, expect, beforeEach } from '@jest/globals';
|
||||||
|
import type { FileKeystore } from '../src/FileKeystore.js'; // Import TYPE only (safe in ESM)
|
||||||
|
|
||||||
|
// We use unstable_mockModule because standard jest.mock struggles to intercept
|
||||||
|
// core Node modules (like fs) in strict ESM mode.
|
||||||
|
jest.unstable_mockModule('fs', () => ({
|
||||||
|
// We must mock the 'default' export because your code uses: import fs from 'fs'
|
||||||
|
default: {
|
||||||
|
existsSync: jest.fn(),
|
||||||
|
mkdirSync: jest.fn(),
|
||||||
|
writeFileSync: jest.fn(),
|
||||||
|
readFileSync: jest.fn(),
|
||||||
|
},
|
||||||
|
// We also mock named exports purely for safety, though 'default' is the key one here
|
||||||
|
existsSync: jest.fn(),
|
||||||
|
mkdirSync: jest.fn(),
|
||||||
|
writeFileSync: jest.fn(),
|
||||||
|
readFileSync: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock dependency
|
||||||
|
jest.unstable_mockModule('@BridgemanAccessible/ba-auth/keystore', () => ({
|
||||||
|
BaseKeystore: class {},
|
||||||
|
Keystore: {
|
||||||
|
addKeystoreType: jest.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('FileKeystore', () => {
|
||||||
|
// Variables to hold our dynamically imported modules
|
||||||
|
let FileKeystoreClass: typeof FileKeystore;
|
||||||
|
let mockFs: any;
|
||||||
|
|
||||||
|
const TEST_DIR = '.test-cert';
|
||||||
|
const TEST_FILE = 'test-keystore.json';
|
||||||
|
const FULL_PATH = path.join(TEST_DIR, TEST_FILE);
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
|
||||||
|
// 3. DYNAMICALLY IMPORT THE MODULES
|
||||||
|
// This is the magic. We import AFTER the mock is defined.
|
||||||
|
// This ensures 'fs' inside FileKeystore.ts is actually our mock.
|
||||||
|
const fsModule = await import('fs');
|
||||||
|
mockFs = fsModule.default; // Access the default export we mocked above
|
||||||
|
|
||||||
|
// Import your class under test
|
||||||
|
// Note: You might need to add '.js' to the path if your resolving is strict
|
||||||
|
const module = await import('../src/FileKeystore.js');
|
||||||
|
FileKeystoreClass = module.FileKeystore;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should create a NEW keystore if the file does not exist', async () => {
|
||||||
|
// SETUP:
|
||||||
|
// 1. Pretend the file does NOT exist
|
||||||
|
mockFs.existsSync.mockReturnValue(false);
|
||||||
|
// 2. Mock writeFileSync to do nothing (just verify it was called)
|
||||||
|
mockFs.writeFileSync.mockImplementation(() => {});
|
||||||
|
// 3. Mock mkdirSync
|
||||||
|
mockFs.mkdirSync.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
// EXECUTE
|
||||||
|
const keystoreInstance = await FileKeystoreClass.create(TEST_FILE, TEST_DIR);
|
||||||
|
|
||||||
|
// VERIFY
|
||||||
|
// 1. Verify it checked for file existence
|
||||||
|
expect(mockFs.existsSync).toHaveBeenCalledWith(FULL_PATH);
|
||||||
|
|
||||||
|
// 2. Verify it checked for directory existence (and created it)
|
||||||
|
expect(mockFs.mkdirSync).toHaveBeenCalledWith(TEST_DIR);
|
||||||
|
|
||||||
|
// 3. Verify it wrote the new keystore to disk
|
||||||
|
// We expect the first argument to be the path, and the second to be a JSON string
|
||||||
|
expect(mockFs.writeFileSync).toHaveBeenCalledWith(
|
||||||
|
FULL_PATH,
|
||||||
|
expect.stringContaining('"keys":') // Simple check to ensure it looks like JSON
|
||||||
|
);
|
||||||
|
|
||||||
|
// 4. Verify the instance path is set correctly
|
||||||
|
expect(keystoreInstance.getKeystoreFilePath()).toBe(FULL_PATH);
|
||||||
|
|
||||||
|
// 5. Verify internal keystore state (node-jose should have generated keys)
|
||||||
|
// @ts-ignore - accessing public property 'keystore' which might be protected in your real usage,
|
||||||
|
// but implied public in the provided snippet logic
|
||||||
|
const keys = keystoreInstance.keystore.all();
|
||||||
|
expect(keys.length).toBeGreaterThanOrEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should LOAD an existing keystore if the file exists', async () => {
|
||||||
|
// SETUP:
|
||||||
|
// 1. Create a real temporary keystore using node-jose to mimic a file on disk
|
||||||
|
const realKeystore = await NodeJose.JWK.createKeyStore().generate('RSA', 2048);
|
||||||
|
const keystoreJson = JSON.stringify(realKeystore.keystore.toJSON(true));
|
||||||
|
|
||||||
|
// 2. Pretend the file DOES exist
|
||||||
|
mockFs.existsSync.mockReturnValue(true);
|
||||||
|
|
||||||
|
// 3. Pretend readFileSync returns our JSON string
|
||||||
|
mockFs.readFileSync.mockReturnValue(keystoreJson);
|
||||||
|
|
||||||
|
// EXECUTE
|
||||||
|
const keystoreInstance = await FileKeystoreClass.create(TEST_FILE, TEST_DIR);
|
||||||
|
|
||||||
|
// VERIFY
|
||||||
|
// 1. It should check existence
|
||||||
|
expect(mockFs.existsSync).toHaveBeenCalledWith(FULL_PATH);
|
||||||
|
|
||||||
|
// 2. It should READ the file
|
||||||
|
expect(mockFs.readFileSync).toHaveBeenCalledWith(FULL_PATH, 'utf-8');
|
||||||
|
|
||||||
|
// 3. CRITICAL: It should NOT overwrite the file
|
||||||
|
expect(mockFs.writeFileSync).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// 4. CRITICAL: It should NOT try to create the directory again
|
||||||
|
// (The code checks !existsSync(file), so it goes into the else block)
|
||||||
|
expect(mockFs.mkdirSync).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// 5. Verify the loaded keys match what we "mocked" on disk
|
||||||
|
// @ts-ignore
|
||||||
|
const loadedKeys = keystoreInstance.keystore.all();
|
||||||
|
expect(loadedKeys.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should create directory only if it is missing', async () => {
|
||||||
|
// Scenario: File missing, Directory also missing
|
||||||
|
mockFs.existsSync
|
||||||
|
.mockReturnValueOnce(false) // First check: File exists? No.
|
||||||
|
.mockReturnValueOnce(false); // Second check: Dir exists? No.
|
||||||
|
|
||||||
|
await FileKeystoreClass.create(TEST_FILE, TEST_DIR);
|
||||||
|
|
||||||
|
expect(mockFs.mkdirSync).toHaveBeenCalledWith(TEST_DIR);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should NOT create directory if it already exists', async () => {
|
||||||
|
// Scenario: File missing, Directory exists
|
||||||
|
mockFs.existsSync
|
||||||
|
.mockReturnValueOnce(false) // First check: File exists? No.
|
||||||
|
.mockReturnValueOnce(true); // Second check: Dir exists? Yes.
|
||||||
|
|
||||||
|
await FileKeystoreClass.create(TEST_FILE, TEST_DIR);
|
||||||
|
|
||||||
|
expect(mockFs.mkdirSync).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
17
tsconfig.build.json
Normal file
17
tsconfig.build.json
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
// Visit https://aka.ms/tsconfig to read more about this file
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"types": [],
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"dist",
|
||||||
|
"tests"
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"./src/**/*.ts",
|
||||||
|
"./src/**/*.tsx"
|
||||||
|
]
|
||||||
|
}
|
||||||
50
tsconfig.json
Normal file
50
tsconfig.json
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
// Visit https://aka.ms/tsconfig to read more about this file
|
||||||
|
"compilerOptions": {
|
||||||
|
// File Layout
|
||||||
|
// "rootDir": "./src",
|
||||||
|
"outDir": "./dist",
|
||||||
|
|
||||||
|
// Environment Settings
|
||||||
|
// See also https://aka.ms/tsconfig/module
|
||||||
|
"module": "nodenext",
|
||||||
|
"target": "esnext",
|
||||||
|
"types": ["node", "jest"],
|
||||||
|
// For nodejs:
|
||||||
|
// "lib": ["esnext"],
|
||||||
|
// "types": ["node"],
|
||||||
|
// and npm install -D @types/node
|
||||||
|
|
||||||
|
// Other Outputs
|
||||||
|
"sourceMap": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
|
||||||
|
// Stricter Typechecking Options
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"exactOptionalPropertyTypes": true,
|
||||||
|
|
||||||
|
// Style Options
|
||||||
|
// "noImplicitReturns": true,
|
||||||
|
// "noImplicitOverride": true,
|
||||||
|
// "noUnusedLocals": true,
|
||||||
|
// "noUnusedParameters": true,
|
||||||
|
// "noFallthroughCasesInSwitch": true,
|
||||||
|
// "noPropertyAccessFromIndexSignature": true,
|
||||||
|
|
||||||
|
// Recommended Options
|
||||||
|
"strict": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noUncheckedSideEffectImports": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "dist"],
|
||||||
|
"include": [
|
||||||
|
"./src/**/*.ts",
|
||||||
|
"./src/**/*.tsx",
|
||||||
|
"./tests/**/*.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue