-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
247 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
import { Test } from '@nestjs/testing'; | ||
import { plainToClass } from 'class-transformer'; | ||
import { ConfigLoaderService } from './config-loader.service'; | ||
import { InMemoryConfiguration } from './in-memory-configuration'; | ||
|
||
const FAKE_FS = { | ||
rabbit: 'rabbit', | ||
wallet: 'wallet', | ||
}; | ||
|
||
const DEFAULTS = { | ||
RPC_URL: 'some-rpc-url', | ||
RABBITMQ_URL: 'some-rabbit-url', | ||
RABBITMQ_LOGIN: 'some-rabbit-login', | ||
}; | ||
|
||
describe('ConfigLoaderService base spec', () => { | ||
let configLoaderService: ConfigLoaderService; | ||
|
||
beforeEach(async () => { | ||
const moduleRef = await Test.createTestingModule({ | ||
imports: [ConfigLoaderService], | ||
}).compile(); | ||
|
||
configLoaderService = moduleRef.get(ConfigLoaderService); | ||
|
||
const cb = (path: string) => { | ||
if (FAKE_FS[path]) return FAKE_FS[path]; | ||
|
||
throw new Error('unknown path'); | ||
}; | ||
jest.spyOn(configLoaderService, 'readFile').mockImplementation(cb); | ||
}); | ||
|
||
test('default behavior', async () => { | ||
const prepConfig = plainToClass(InMemoryConfiguration, { | ||
RABBITMQ_PASSCODE: 'some-rabbit-passcode', | ||
...DEFAULTS, | ||
}); | ||
|
||
await expect(() => | ||
configLoaderService.loadSecrets(prepConfig), | ||
).not.toThrowError(); | ||
}); | ||
|
||
describe('rabbit mq', () => { | ||
let configLoaderService: ConfigLoaderService; | ||
|
||
beforeEach(async () => { | ||
const moduleRef = await Test.createTestingModule({ | ||
imports: [ConfigLoaderService], | ||
}).compile(); | ||
|
||
configLoaderService = moduleRef.get(ConfigLoaderService); | ||
|
||
const cb = (path: string) => { | ||
if (FAKE_FS[path]) return FAKE_FS[path]; | ||
|
||
throw new Error('unknown path'); | ||
}; | ||
jest.spyOn(configLoaderService, 'readFile').mockImplementation(cb); | ||
}); | ||
|
||
test('passcode in file negative', async () => { | ||
const prepConfig = plainToClass(InMemoryConfiguration, { | ||
RABBITMQ_PASSCODE_FILE: 'unreal path', | ||
...DEFAULTS, | ||
}); | ||
|
||
await expect(() => | ||
configLoaderService.loadSecrets(prepConfig), | ||
).rejects.toThrow('unknown path'); | ||
}); | ||
|
||
test('passcode in env positive', async () => { | ||
const prepConfig = plainToClass(InMemoryConfiguration, { | ||
RABBITMQ_PASSCODE: 'env-rabbit', | ||
...DEFAULTS, | ||
}); | ||
const config = await configLoaderService.loadSecrets(prepConfig); | ||
|
||
expect(config).toHaveProperty('RABBITMQ_PASSCODE', 'env-rabbit'); | ||
}); | ||
|
||
test('passcode in file positive', async () => { | ||
const prepConfig = plainToClass(InMemoryConfiguration, { | ||
RABBITMQ_PASSCODE_FILE: 'rabbit', | ||
...DEFAULTS, | ||
}); | ||
const config = await configLoaderService.loadSecrets(prepConfig); | ||
|
||
expect(config).toHaveProperty('RABBITMQ_PASSCODE', 'rabbit'); | ||
}); | ||
|
||
test('passcode in file order _FILE', async () => { | ||
const prepConfig = plainToClass(InMemoryConfiguration, { | ||
RABBITMQ_PASSCODE_FILE: 'rabbit', | ||
RABBITMQ_PASSCODE: 'some-rabbit-passcode', | ||
...DEFAULTS, | ||
}); | ||
|
||
const config = await configLoaderService.loadSecrets(prepConfig); | ||
expect(config).toHaveProperty('RABBITMQ_PASSCODE', 'rabbit'); | ||
}); | ||
}); | ||
|
||
describe('wallet', () => { | ||
let configLoaderService: ConfigLoaderService; | ||
const DEFAULTS_WITH_RABBIT = { | ||
...DEFAULTS, | ||
RABBITMQ_PASSCODE: 'some-rabbit-passcode', | ||
}; | ||
|
||
beforeEach(async () => { | ||
const moduleRef = await Test.createTestingModule({ | ||
imports: [ConfigLoaderService], | ||
}).compile(); | ||
|
||
configLoaderService = moduleRef.get(ConfigLoaderService); | ||
|
||
const cb = (path: string) => { | ||
if (FAKE_FS[path]) return FAKE_FS[path]; | ||
|
||
throw new Error('unknown path'); | ||
}; | ||
jest.spyOn(configLoaderService, 'readFile').mockImplementation(cb); | ||
}); | ||
|
||
test('passcode in file negative', async () => { | ||
const prepConfig = plainToClass(InMemoryConfiguration, { | ||
WALLET_PRIVATE_KEY_FILE: 'unreal path', | ||
...DEFAULTS_WITH_RABBIT, | ||
}); | ||
|
||
await expect(() => | ||
configLoaderService.loadSecrets(prepConfig), | ||
).rejects.toThrow('unknown path'); | ||
}); | ||
|
||
test('passcode in env positive', async () => { | ||
const prepConfig = plainToClass(InMemoryConfiguration, { | ||
WALLET_PRIVATE_KEY: 'env-wallet', | ||
...DEFAULTS_WITH_RABBIT, | ||
}); | ||
const config = await configLoaderService.loadSecrets(prepConfig); | ||
|
||
expect(config).toHaveProperty('WALLET_PRIVATE_KEY', 'env-wallet'); | ||
}); | ||
|
||
test('passcode in file positive', async () => { | ||
const prepConfig = plainToClass(InMemoryConfiguration, { | ||
WALLET_PRIVATE_KEY_FILE: 'wallet', | ||
...DEFAULTS_WITH_RABBIT, | ||
}); | ||
const config = await configLoaderService.loadSecrets(prepConfig); | ||
|
||
expect(config).toHaveProperty('WALLET_PRIVATE_KEY', 'wallet'); | ||
}); | ||
|
||
test('passcode in file order _FILE', async () => { | ||
const prepConfig = plainToClass(InMemoryConfiguration, { | ||
WALLET_PRIVATE_KEY_FILE: 'wallet', | ||
WALLET_PRIVATE_KEY: 'some-wallet-passcode', | ||
...DEFAULTS_WITH_RABBIT, | ||
}); | ||
|
||
const config = await configLoaderService.loadSecrets(prepConfig); | ||
expect(config).toHaveProperty('WALLET_PRIVATE_KEY', 'wallet'); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { readFile } from 'fs/promises'; | ||
|
||
import { InMemoryConfiguration } from './in-memory-configuration'; | ||
import { validateOrReject } from 'class-validator'; | ||
|
||
@Injectable() | ||
export class ConfigLoaderService { | ||
public async readFile(filePath: string) { | ||
return await readFile(filePath, 'utf-8'); | ||
} | ||
|
||
public async loadFile(filePath: string, envVarFile: string) { | ||
try { | ||
const fileContent = (await this.readFile(filePath)) | ||
.toString() | ||
.replace(/(\r\n|\n|\r)/gm, ''); | ||
return fileContent; | ||
} catch (error) { | ||
const errorCode = (error as any).code; | ||
|
||
switch (errorCode) { | ||
case 'ENOENT': | ||
throw new Error(`Failed to load ENV variable from the ${envVarFile}`); | ||
case 'EACCES': | ||
throw new Error( | ||
`Permission denied when trying to read the file specified by ${envVarFile}`, | ||
); | ||
case 'EMFILE': | ||
throw new Error( | ||
`Too many open files in the system when trying to read the file specified by ${envVarFile}`, | ||
); | ||
default: | ||
throw error; | ||
} | ||
} | ||
} | ||
|
||
public async loadEnvOrFile( | ||
config: InMemoryConfiguration, | ||
envName: string, | ||
): Promise<string> { | ||
const envVarFile = envName + '_FILE'; | ||
const filePath = config[envVarFile]; | ||
|
||
if (filePath) { | ||
return await this.loadFile(filePath, envVarFile); | ||
} | ||
|
||
return config[envName]; | ||
} | ||
|
||
public async loadSecrets( | ||
config: InMemoryConfiguration, | ||
): Promise<InMemoryConfiguration> { | ||
config.RABBITMQ_PASSCODE = await this.loadEnvOrFile( | ||
config, | ||
'RABBITMQ_PASSCODE', | ||
); | ||
config.WALLET_PRIVATE_KEY = await this.loadEnvOrFile( | ||
config, | ||
'WALLET_PRIVATE_KEY', | ||
); | ||
|
||
await validateOrReject(config, { | ||
validationError: { target: false, value: false }, | ||
}); | ||
|
||
return config; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters