config-loader: MergedConfigSource tests + fixes
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -18,7 +18,7 @@ import { Config, ConfigReader } from '@backstage/config';
|
||||
import parseArgs from 'minimist';
|
||||
import { EnvConfigSource } from './EnvConfigSource';
|
||||
import { FileConfigSource } from './FileConfigSource';
|
||||
import { MergeConfigSource } from './MergedConfigSource';
|
||||
import { MergedConfigSource } from './MergedConfigSource';
|
||||
import { RemoteConfigSource } from './RemoteConfigSource';
|
||||
import { ConfigSource } from './types';
|
||||
import { ObservableConfigProxy } from './ObservableConfigProxy';
|
||||
@@ -63,7 +63,7 @@ export class ConfigSources {
|
||||
}
|
||||
|
||||
static merge(sources: ConfigSource[]): ConfigSource {
|
||||
return MergeConfigSource.fromConfigSources(sources);
|
||||
return MergedConfigSource.from(sources);
|
||||
}
|
||||
|
||||
static toConfig(source: ConfigSource): Promise<LiveConfig> {
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { MergedConfigSource } from './MergedConfigSource';
|
||||
import { MutableConfigSource } from './MutableConfigSource';
|
||||
import { isResolved, readAll, simpleSource } from './__testUtils__/testUtils';
|
||||
|
||||
describe('MergeConfigSource', () => {
|
||||
it('should forward from a single source', async () => {
|
||||
const source = simpleSource([{ a: 1 }, { a: 2 }, { a: 3 }]);
|
||||
const merged = MergedConfigSource.from([source]);
|
||||
await expect(readAll(merged)).resolves.toEqual([
|
||||
[{ data: { a: 1 }, context: 'mock-source' }],
|
||||
[{ data: { a: 2 }, context: 'mock-source' }],
|
||||
[{ data: { a: 3 }, context: 'mock-source' }],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should forward from multiple sources', async () => {
|
||||
const sourceA = simpleSource([{ a: 1 }, { a: 2 }, { a: 3 }], 'a');
|
||||
const sourceB = simpleSource([{ b: 1 }, { b: 2 }], 'b');
|
||||
const sourceC = simpleSource([{ c: 1 }], 'c');
|
||||
const merged = MergedConfigSource.from([sourceA, sourceB, sourceC]);
|
||||
await expect(readAll(merged)).resolves.toEqual([
|
||||
[
|
||||
{ data: { a: 1 }, context: 'a' },
|
||||
{ data: { b: 1 }, context: 'b' },
|
||||
{ data: { c: 1 }, context: 'c' },
|
||||
],
|
||||
[
|
||||
{ data: { a: 2 }, context: 'a' },
|
||||
{ data: { b: 1 }, context: 'b' },
|
||||
{ data: { c: 1 }, context: 'c' },
|
||||
],
|
||||
[
|
||||
{ data: { a: 3 }, context: 'a' },
|
||||
{ data: { b: 1 }, context: 'b' },
|
||||
{ data: { c: 1 }, context: 'c' },
|
||||
],
|
||||
[
|
||||
{ data: { a: 3 }, context: 'a' },
|
||||
{ data: { b: 2 }, context: 'b' },
|
||||
{ data: { c: 1 }, context: 'c' },
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should forward from multiple sources at difference pace', async () => {
|
||||
const sourceA = MutableConfigSource.create({ context: 'a' });
|
||||
const sourceB = MutableConfigSource.create({ context: 'b' });
|
||||
const merged = MergedConfigSource.from([sourceA, sourceB]);
|
||||
|
||||
const it = merged.readConfigData();
|
||||
|
||||
const first = it.next();
|
||||
await expect(isResolved(first, { wait: true })).resolves.toBe(false);
|
||||
sourceA.setData({ a: 1 });
|
||||
await expect(isResolved(first, { wait: true })).resolves.toBe(false);
|
||||
sourceB.setData({ b: 1 });
|
||||
|
||||
await expect(first).resolves.toEqual({
|
||||
value: {
|
||||
data: [
|
||||
{ data: { a: 1 }, context: 'a' },
|
||||
{ data: { b: 1 }, context: 'b' },
|
||||
],
|
||||
},
|
||||
done: false,
|
||||
});
|
||||
|
||||
sourceB.setData({ b: 2 });
|
||||
|
||||
await expect(it.next()).resolves.toEqual({
|
||||
value: {
|
||||
data: [
|
||||
{ data: { a: 1 }, context: 'a' },
|
||||
{ data: { b: 2 }, context: 'b' },
|
||||
],
|
||||
},
|
||||
done: false,
|
||||
});
|
||||
|
||||
const last = it.next();
|
||||
await expect(isResolved(last, { wait: true })).resolves.toBe(false);
|
||||
sourceA.close();
|
||||
await expect(isResolved(last, { wait: true })).resolves.toBe(false);
|
||||
sourceB.close();
|
||||
await expect(isResolved(last, { wait: true })).resolves.toBe(true);
|
||||
|
||||
await expect(last).resolves.toEqual({
|
||||
done: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -21,9 +21,9 @@ import {
|
||||
ReadConfigDataOptions,
|
||||
} from './types';
|
||||
|
||||
export class MergeConfigSource implements ConfigSource {
|
||||
static fromConfigSources(sources: ConfigSource[]): ConfigSource {
|
||||
return new MergeConfigSource(sources);
|
||||
export class MergedConfigSource implements ConfigSource {
|
||||
static from(sources: ConfigSource[]): ConfigSource {
|
||||
return new MergedConfigSource(sources);
|
||||
}
|
||||
|
||||
private constructor(private readonly sources: ConfigSource[]) {}
|
||||
@@ -53,7 +53,7 @@ export class MergeConfigSource implements ConfigSource {
|
||||
|
||||
while (results.some(Boolean)) {
|
||||
try {
|
||||
const [i, result] = (await Promise.race(results))!;
|
||||
const [i, result] = (await Promise.race(results.filter(Boolean)))!;
|
||||
if (result.done) {
|
||||
results[i] = undefined;
|
||||
} else {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { ConfigSource, ConfigSourceData } from '../types';
|
||||
|
||||
export function isResolved(
|
||||
@@ -49,3 +50,16 @@ export async function readAll(
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
export function simpleSource(
|
||||
data: JsonObject[],
|
||||
context: string = 'mock-source',
|
||||
): ConfigSource {
|
||||
return {
|
||||
async *readConfigData() {
|
||||
for (const d of data) {
|
||||
yield { data: [{ data: d, context }] };
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user