Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: added tests for the configuration package #588

Merged
merged 11 commits into from
Dec 24, 2024
2 changes: 1 addition & 1 deletion .sonar-project.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
sonar.projectKey=jitar
sonar.projectName=jitar
sonar.langugage=typescript
sonar.language=typescript

# Path to sources
sonar.sources=packages/**/src
Expand Down
4 changes: 3 additions & 1 deletion packages/configuration/src/ConfigurationManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

import { Validator } from '@jitar/validation';
import { LocalFileManager } from '@jitar/sourcing';

import { EnvironmentConfigurator } from './environment';
import { RuntimeConfiguration, RuntimeConfigurationBuilder } from './runtime';
Expand All @@ -17,7 +18,8 @@ export default class ConfigurationManager

constructor(rootPath: string = DEFAULT_ROOT_PATH)
{
const reader = new ConfigurationReader(rootPath);
const fileManager = new LocalFileManager(rootPath);
const reader = new ConfigurationReader(fileManager);
const validator = new Validator();

this.#environmentConfigurator = new EnvironmentConfigurator();
Expand Down
1 change: 1 addition & 0 deletions packages/configuration/src/runtime/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

export { default as RuntimeConfigurationInvalid } from './errors/RuntimeConfigurationInvalid';
export { default as RuntimeConfiguration } from './definitions/RuntimeConfiguration';
export { default as RuntimeConfigurationBuilder } from './ConfigurationBuilder';
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { ValidationScheme } from '@jitar/validation';

type GatewayConfiguration =
{
monitor: number;
monitor?: number;
trustKey?: string;
};

Expand Down
2 changes: 2 additions & 0 deletions packages/configuration/src/server/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

export { default as ServerConfigurationInvalid } from './errors/ServerConfigurationInvalid';

export { default as ServerConfiguration } from './definitions/ServerConfiguration';
export { default as StandaloneConfiguration } from './definitions/StandaloneConfiguration';
export { default as ProxyConfiguration } from './definitions/ProxyConfiguration';
Expand Down
25 changes: 12 additions & 13 deletions packages/configuration/src/utils/ConfigurationReader.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@

import { LocalFileManager } from '@jitar/sourcing';
import type { FileManager } from '@jitar/sourcing';

import InvalidFileType from './errors/InvalidConfigurationFile';

const ENVIRONMENT_VARIABLE_REGEX = /\${([^}]*)}/g;

export default class ConfigurationReader
{
readonly #fileManager: FileManager;

constructor(rootPath: string)
constructor(fileManager: FileManager)
{
this.#fileManager = new LocalFileManager(rootPath);
this.#fileManager = fileManager;
}

async read(filename: string): Promise<Record<string, unknown>>
Expand All @@ -23,12 +24,16 @@ export default class ConfigurationReader
}

const file = await this.#fileManager.read(filename);

if (file.type.includes('json') === false)
{
throw new InvalidFileType(filename);
}

const content = file.content.toString();
const configuration = this.#replaceEnvironmentVariables(content);

return file.type.includes('json')
? this.#parseJson(configuration)
: this.#parseText(configuration);

return this.#parseJson(configuration);
}

#replaceEnvironmentVariables(content: string): string
Expand All @@ -43,10 +48,4 @@ export default class ConfigurationReader
{
return JSON.parse(configuration);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
#parseText(configuration: string): Record<string, unknown>
{
return {};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

export default class InvalidConfigurationFile extends Error
{
constructor(filename: string)
{
super(`${filename} is not a valid configuration file.`);
}
}
1 change: 1 addition & 0 deletions packages/configuration/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@

export { default as InvalidConfigurationFile } from './errors/InvalidConfigurationFile';
export { default as ConfigurationReader } from './ConfigurationReader';
12 changes: 0 additions & 12 deletions packages/configuration/test/dummy.spec.ts

This file was deleted.

10 changes: 10 additions & 0 deletions packages/configuration/test/environment/Configurator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

import { describe, expect, it } from 'vitest';

describe('Configurator', () =>
{
it('should not test standard library functions', async () =>
{
expect(true).toBeTruthy();
});
});
86 changes: 86 additions & 0 deletions packages/configuration/test/fixtures/fileManager.fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@

import { File, FileManager } from '@jitar/sourcing';

export default class TestFileManager implements FileManager
{
readonly #files: Record<string, File>;

constructor(files: Record<string, File>)
{
this.#files = files;
}

getRootLocation(): string
{
throw new Error('Method not implemented.');
}

getAbsoluteLocation(filename: string): string
{
throw new Error('Method not implemented.');
}

getRelativeLocation(filename: string): string
{
throw new Error('Method not implemented.');
}

getType(filename: string): Promise<string>
{
throw new Error('Method not implemented.');
}

getContent(filename: string): Promise<Buffer | string>
{
throw new Error('Method not implemented.');
}

exists(filename: string): Promise<boolean>
{
for (const key in this.#files)
{
const value = this.#files[key];

if (value.location === filename)
{
return Promise.resolve(true);
}
}

return Promise.resolve(false);
}

read(filename: string): Promise<File>
{
let file: File | undefined;

for (const key in this.#files)
{
const value = this.#files[key];

if (value.location === filename)
{
file = value;

break;
}
}

return Promise.resolve(file as File);
}

write(filename: string, content: string): Promise<void>
{
throw new Error('Method not implemented.');
}

delete(filename: string): Promise<void>
{
throw new Error('Method not implemented.');
}

filter(pattern: string): Promise<string[]>
{
throw new Error('Method not implemented.');
}
}
2 changes: 2 additions & 0 deletions packages/configuration/test/fixtures/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

export { default as FileManager } from './fileManager.fixture';
35 changes: 35 additions & 0 deletions packages/configuration/test/runtime/ConfigurationBuilder.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

import { describe, expect, it } from 'vitest';

import { configurationBuilder, FILENAMES, CONFIGURATIONS, RuntimeConfigurationInvalid, VALIDATION_RESULT } from './fixtures';

describe('runtime/ConfigurationBuilder', () =>
{
it('should build a default runtime configuration without configuration file', async () =>
{
const promise = configurationBuilder.build();

await expect(promise).resolves.toEqual(CONFIGURATIONS.DEFAULT);
});

it('should build a valid runtime configuration from a valid file', async () =>
{
const promise = configurationBuilder.build(FILENAMES.VALID);

await expect(promise).resolves.toEqual(CONFIGURATIONS.RUNTIME);
});

it('should build a default runtime when the configuration file does not exist', async () =>
{
const promise = configurationBuilder.build(FILENAMES.MISSING);

await expect(promise).resolves.toEqual(CONFIGURATIONS.DEFAULT);
});

it('should reject an invalid runtime configuration', async () =>
{
const promise = configurationBuilder.build(FILENAMES.INVALID);

await expect(promise).rejects.toEqual(new RuntimeConfigurationInvalid(VALIDATION_RESULT));
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

import { RuntimeConfiguration } from '../../../src/runtime';

const defaultConfiguration: RuntimeConfiguration =
{
source: './src',
target: './dist',
} as const;

const runtimeConfiguration: RuntimeConfiguration =
{
source: './source',
target: './target',
} as const;

const invalidConfiguration: any =
{
invalid: true
} as const;

export const CONFIGURATIONS: Record<string, RuntimeConfiguration> =
{
DEFAULT: defaultConfiguration,
RUNTIME: runtimeConfiguration,
INVALID: invalidConfiguration,
} as const;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

export const FILENAMES = {
DEFAULT: './jitar.json',
VALID: 'valid-runtime-configuration.json',
INVALID: 'invalid-runtime-configuration.json',
MISSING: 'missing-runtime-configuration.json',
} as const;
12 changes: 12 additions & 0 deletions packages/configuration/test/runtime/fixtures/files.fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

import { File } from '@jitar/sourcing';

import { CONFIGURATIONS } from './configuration.fixture';
import { FILENAMES } from './filenames.fixture';

export const FILES: Record<string, File> =
{
DEFAULT: new File(FILENAMES.DEFAULT, 'text/json', JSON.stringify(CONFIGURATIONS.DEFAULT)),
VALID: new File(FILENAMES.VALID, 'text/json', JSON.stringify(CONFIGURATIONS.RUNTIME)),
INVALID: new File(FILENAMES.INVALID, 'text/json', JSON.stringify(CONFIGURATIONS.INVALID))
} as const;
21 changes: 21 additions & 0 deletions packages/configuration/test/runtime/fixtures/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

import { Validator } from '@jitar/validation';

import { RuntimeConfigurationBuilder } from '../../../src/runtime';
import RuntimeConfigurationInvalid from '../../../src/runtime/errors/RuntimeConfigurationInvalid';
import { ConfigurationReader } from '../../../src/utils';

import { FileManager } from '../../fixtures';

import { FILES } from './files.fixture';
import { FILENAMES } from './filenames.fixture';
import { CONFIGURATIONS } from './configuration.fixture';
import { VALIDATION_RESULT } from './validation.fixture';

const fileManager = new FileManager(FILES);
const configurationReader = new ConfigurationReader(fileManager);
const validator = new Validator();

const configurationBuilder = new RuntimeConfigurationBuilder(configurationReader, validator);

export { configurationBuilder, FILENAMES, CONFIGURATIONS, RuntimeConfigurationInvalid, VALIDATION_RESULT };
12 changes: 12 additions & 0 deletions packages/configuration/test/runtime/fixtures/validation.fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

import { ValidationResult } from "@jitar/validation";

const VALIDATION_RESULT: ValidationResult =
{
valid: false,
errors: [
"Unknown field 'invalid'",
]
} as const;

export { VALIDATION_RESULT };
21 changes: 21 additions & 0 deletions packages/configuration/test/server/ConfigurationBuilder.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

import { describe, expect, it } from 'vitest';

import { configurationBuilder, FILENAMES, SERVER_CONFIGURATION, ServerConfigurationInvalid, VALIDATION_RESULT } from './fixtures';

describe('server/ConfigurationBuilder', () =>
{
it('should build a valid server configuration', async () =>
{
const promise = configurationBuilder.build(FILENAMES.VALID_CONFIGURATION);

await expect(promise).resolves.toEqual(SERVER_CONFIGURATION);
});

it('should reject an invalid server configuration', async () =>
{
const promise = configurationBuilder.build(FILENAMES.INVALID_CONFIGURATION);

await expect(promise).rejects.toEqual(new ServerConfigurationInvalid(VALIDATION_RESULT));
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

import type { GatewayConfiguration, ProxyConfiguration, RepositoryConfiguration, ServerConfiguration, StandaloneConfiguration, WorkerConfiguration } from '../../../src/server';

const serveIndexOnNotFound = true;
const assets = ['index.html', 'favicon.ico'];
const segments = ['segment'];
const indexFilename = 'index.html';
const trustKey = 'trust-key';
const gateway = 'https://gateway';
const repository = 'https://repository';

const gatewayConfiguration: GatewayConfiguration = { monitor: 5000, trustKey } as const;
const proxyConfiguration: ProxyConfiguration = { gateway, repository } as const;
const repositoryConfiguration: RepositoryConfiguration = { indexFilename, serveIndexOnNotFound, assets } as const;
const standaloneConfiguration: StandaloneConfiguration = { segments, indexFilename, serveIndexOnNotFound, assets } as const;
const workerConfiguration: WorkerConfiguration = { gateway, segments, trustKey } as const;

export const SERVER_CONFIGURATION: ServerConfiguration =
{
url: 'https://server',
setUp: ['setup'],
tearDown: ['tearDown'],
middleware: ['middleware'],
healthChecks: ['healthChecks'],

gateway: gatewayConfiguration,
proxy: proxyConfiguration,
repository: repositoryConfiguration,
standalone: standaloneConfiguration,
worker: workerConfiguration
} as const;
Loading
Loading