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
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
"scripts": {
"clean": "rimraf dist/",
"clean:all": "rimraf dist/ node_modules/ package-lock.json",
"lint": "eslint --fix './src/**/*.ts'",
"format": "prettier --write 'src/**/*.ts'",
"build": "npm run clean && npm run lint && npm run format && tsc",
"lint": "eslint './src/**/*.ts'",
"lint:fix": "eslint --fix './src/**/*.ts'",
"build": "npm run clean && tsc",
"docker": "docker-compose up --force-recreate -d",
"docker:kill": "docker-compose kill",
"precopy-test-config": "rimraf dist/tests/integration/config",
Expand Down
35 changes: 35 additions & 0 deletions src/main/DynamicConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export class DynamicConfig implements IDynamicConfig {
private translator: ITranslator
private schemas: ISchemaMap
private errorMap: errors.IConfigErrorMap
private configEnv: string | undefined
private mockConfig: IRootConfigValue | null

/**
* The observerMap is a cache of the Observer for a specified key. There is no need to create
Expand All @@ -74,8 +76,10 @@ export class DynamicConfig implements IDynamicConfig {
}: IConfigOptions = {}) {
this.errorMap = {}
this.promisedConfig = null
this.mockConfig = null
this.schemas = schemas
this.translator = ConfigUtils.makeTranslator(translators)
this.configEnv = configEnv
this.configLoader = new ConfigLoader({
loaders,
configPath,
Expand Down Expand Up @@ -106,6 +110,33 @@ export class DynamicConfig implements IDynamicConfig {
}
}

private assertMockEnvironment(): void {
if (this.configEnv !== 'test' && this.configEnv !== 'development') {
throw new Error(
`Config mocks are only enabled for 'test' and 'development' environments.`,
)
}
}

public injectMockConfig(obj: unknown): void {
this.assertMockEnvironment()

const mockConfig: IRootConfigValue = ConfigBuilder.createConfigObject(
{
type: 'local',
name: 'mock',
},
obj,
)

this.mockConfig = mockConfig
}

public resetMockConfig(): void {
this.assertMockEnvironment()
this.mockConfig = null
}

/**
* Gets a given key from the config. There are not guarantees that the config is already
* loaded, so we must return a Promise.
Expand Down Expand Up @@ -623,6 +654,10 @@ export class DynamicConfig implements IDynamicConfig {
}

private getConfig(): Promise<IRootConfigValue> {
if (this.mockConfig != null) {
return Promise.resolve(this.mockConfig)
}

if (this.promisedConfig === null) {
this.promisedConfig = this.loadConfigs().then(
async (loadedConfigs: IRootConfigValue) => {
Expand Down
116 changes: 113 additions & 3 deletions src/tests/integration/DynamicConfig.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,9 +316,7 @@ describe('DynamicConfig', () => {
'http://localhost:8510',
])

const initialConfigValue = await dynamicConfig.get(
'secret',
)
const initialConfigValue = await dynamicConfig.get('secret')

expect(initialConfigValue).to.equal('this is a secret')

Expand Down Expand Up @@ -1789,4 +1787,116 @@ describe('DynamicConfig', () => {
})
})
})

describe('When using mock config', () => {
before(async () => {
process.env.NOT_NULLABLE = 'NOT_NULLABLE'
})

after(async () => {
delete process.env.NOT_NULLABLE
})

describe('injectMockConfig', () => {
it('should successfully inject mock config', async () => {
const dynamicConfig: DynamicConfig = new DynamicConfig({
configEnv: 'test',
configPath: path.resolve(__dirname, './config'),
loaders: [jsonLoader],
translators: [envTranslator],
})

dynamicConfig.injectMockConfig({
serviceName: 'mock-service',
clients: {
'foo-service': {
host: 'localhost',
port: 8080,
},
},
})

const clients = await dynamicConfig.get('clients')
const serviceName = await dynamicConfig.get('serviceName')

expect(clients).to.equal({
'foo-service': {
host: 'localhost',
port: 8080,
},
})

expect(serviceName).to.equal('mock-service')

dynamicConfig.resetMockConfig()

expect(await dynamicConfig.get()).to.equal({
'test-service': {
destination: 'http://localhost:8080',
},
names: {
first: ['Bob', 'Helen', 'Joe', 'Jane'],
last: ['Smith', 'Warren', 'Malick'],
},
nullable_test: {
not_nullable: 'NOT_NULLABLE',
nullable: null,
},
persistedQueries: {
databaseLookup: {
password: 'root',
shardedDBHostsInfo: {
sharding: {
client: {
'shard-info': {
'shard-count': 4,
'shard-map': [
{
'virtual-end': 3,
'virtual-start': 0,
destination:
'localhost:4141',
},
],
},
},
},
},
username: 'root',
},
},
project: {
health: {
control: '/control',
response: 'PASS',
},
id: {
name: 'test-project',
ref: 987860,
},
},
secret: "I'm not secret",
server: {
host: 'localhost',
port: 8000,
},
type_test: true,
version: '2.0.1',
})
})

it(`should throw if not 'test' or 'development' environment`, async () => {
const dynamicConfig: DynamicConfig = new DynamicConfig({
configEnv: 'production',
configPath: path.resolve(__dirname, './config'),
loaders: [jsonLoader],
translators: [envTranslator],
})

expect(() => {
dynamicConfig.injectMockConfig({})
}).to.throw()
})
})
})
})
Loading