This commit unifies the concepts of executables having same ID structure. It paves the way for more complex ID structure and using IDs in collection files as part of new ID solution (#262). Using string IDs also leads to more expressive test code. This commit also refactors the rest of the code to adopt to the changes. This commit: - Separate concerns from entities for data access (in repositories) and executables. Executables use `Identifiable` meanwhile repositories use `RepositoryEntity`. - Refactor unnecessary generic parameters for enttities and ids, enforcing string gtype everwyhere. - Changes numeric IDs to string IDs for categories to unify the retrieval and construction for executables, using pseudo-ids (their names) just like scripts. - Remove `BaseEntity` for simplicity. - Simplify usage and construction of executable objects. Move factories responsible for creation of category/scripts to domain layer. Do not longer export `CollectionCategorY` and `CollectionScript`. - Use named typed for string IDs for better differentation of different ID contexts in code.
198 lines
7.7 KiB
TypeScript
198 lines
7.7 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import type { CollectionData } from '@/application/collections/';
|
|
import { parseProjectDetails } from '@/application/Parser/ProjectDetailsParser';
|
|
import { parseApplication } from '@/application/Parser/ApplicationParser';
|
|
import WindowsData from '@/application/collections/windows.yaml';
|
|
import MacOsData from '@/application/collections/macos.yaml';
|
|
import LinuxData from '@/application/collections/linux.yaml';
|
|
import { OperatingSystem } from '@/domain/OperatingSystem';
|
|
import { CategoryCollectionStub } from '@tests/unit/shared/Stubs/CategoryCollectionStub';
|
|
import { CollectionDataStub } from '@tests/unit/shared/Stubs/CollectionDataStub';
|
|
import { CategoryCollectionParserStub } from '@tests/unit/shared/Stubs/CategoryCollectionParserStub';
|
|
import { ProjectDetailsParserStub } from '@tests/unit/shared/Stubs/ProjectDetailsParserStub';
|
|
import { ProjectDetailsStub } from '@tests/unit/shared/Stubs/ProjectDetailsStub';
|
|
import type { CategoryCollectionParser } from '@/application/Parser/CategoryCollectionParser';
|
|
import type { NonEmptyCollectionAssertion, TypeValidator } from '@/application/Parser/Common/TypeValidator';
|
|
import { TypeValidatorStub } from '@tests/unit/shared/Stubs/TypeValidatorStub';
|
|
import type { ICategoryCollection } from '@/domain/Collection/ICategoryCollection';
|
|
|
|
describe('ApplicationParser', () => {
|
|
describe('parseApplication', () => {
|
|
describe('categoryParser', () => {
|
|
it('returns result from the parser', () => {
|
|
// arrange
|
|
const os = OperatingSystem.macOS;
|
|
const data = new CollectionDataStub();
|
|
const expected = new CategoryCollectionStub()
|
|
.withOs(os);
|
|
const parser = new CategoryCollectionParserStub()
|
|
.withReturnValue(data, expected)
|
|
.getStub();
|
|
const sut = new ApplicationParserBuilder()
|
|
.withCategoryCollectionParser(parser)
|
|
.withCollectionsData([data]);
|
|
// act
|
|
const app = sut.parseApplication();
|
|
// assert
|
|
const actual = app.getCollection(os);
|
|
expect(expected).to.equal(actual);
|
|
});
|
|
});
|
|
describe('projectDetails', () => {
|
|
it('projectDetailsParser is used to create the instance', () => {
|
|
// arrange
|
|
const expectedProjectDetails = new ProjectDetailsStub();
|
|
const projectDetailsParserStub = new ProjectDetailsParserStub()
|
|
.withReturnValue(expectedProjectDetails);
|
|
const sut = new ApplicationParserBuilder()
|
|
.withProjectDetailsParser(projectDetailsParserStub.getStub());
|
|
// act
|
|
const app = sut.parseApplication();
|
|
// assert
|
|
const actualProjectDetails = app.projectDetails;
|
|
expect(expectedProjectDetails).to.deep.equal(actualProjectDetails);
|
|
});
|
|
it('projectDetailsParser is used to parse collection', () => {
|
|
// arrange
|
|
const expectedProjectDetails = new ProjectDetailsStub();
|
|
const projectDetailsParserStub = new ProjectDetailsParserStub()
|
|
.withReturnValue(expectedProjectDetails);
|
|
const collectionParserStub = new CategoryCollectionParserStub();
|
|
const sut = new ApplicationParserBuilder()
|
|
.withProjectDetailsParser(projectDetailsParserStub.getStub())
|
|
.withCategoryCollectionParser(collectionParserStub.getStub());
|
|
// act
|
|
sut.parseApplication();
|
|
// assert
|
|
expect(collectionParserStub.arguments).to.have.length.above(0);
|
|
const actuallyUsedInfos = collectionParserStub.arguments.map((arg) => arg.projectDetails);
|
|
expect(actuallyUsedInfos.every(
|
|
(actualProjectDetails) => actualProjectDetails === expectedProjectDetails,
|
|
)).to.equal(true);
|
|
});
|
|
});
|
|
describe('collectionsData', () => {
|
|
describe('set as expected', () => {
|
|
// arrange
|
|
const testScenarios: {
|
|
readonly description: string;
|
|
readonly input: readonly CollectionData[];
|
|
readonly output: readonly ICategoryCollection[];
|
|
}[] = [
|
|
{
|
|
description: 'single collection',
|
|
input: [new CollectionDataStub()],
|
|
output: [new CategoryCollectionStub().withOs(OperatingSystem.macOS)],
|
|
},
|
|
{
|
|
description: 'multiple collections',
|
|
input: [
|
|
new CollectionDataStub().withOs('windows'),
|
|
new CollectionDataStub().withOs('macos'),
|
|
],
|
|
output: [
|
|
new CategoryCollectionStub().withOs(OperatingSystem.macOS),
|
|
new CategoryCollectionStub().withOs(OperatingSystem.Windows),
|
|
],
|
|
},
|
|
];
|
|
// act
|
|
testScenarios.forEach(({
|
|
description, input, output,
|
|
}) => {
|
|
it(description, () => {
|
|
let categoryParserStub = new CategoryCollectionParserStub();
|
|
for (let i = 0; i < input.length; i++) {
|
|
categoryParserStub = categoryParserStub
|
|
.withReturnValue(input[i], output[i]);
|
|
}
|
|
const sut = new ApplicationParserBuilder()
|
|
.withCategoryCollectionParser(categoryParserStub.getStub())
|
|
.withCollectionsData(input);
|
|
// act
|
|
const app = sut.parseApplication();
|
|
// assert
|
|
expect(app.collections).to.deep.equal(output);
|
|
});
|
|
});
|
|
});
|
|
it('defaults to expected data', () => {
|
|
// arrange
|
|
const expected = [WindowsData, MacOsData, LinuxData];
|
|
const categoryParserStub = new CategoryCollectionParserStub();
|
|
const sut = new ApplicationParserBuilder()
|
|
.withCollectionsData(undefined)
|
|
.withCategoryCollectionParser(categoryParserStub.getStub());
|
|
// act
|
|
sut.parseApplication();
|
|
// assert
|
|
const actual = categoryParserStub.arguments.map((args) => args.data);
|
|
expect(actual).to.deep.equal(expected);
|
|
});
|
|
it('validates non empty array', () => {
|
|
// arrange
|
|
const data = [new CollectionDataStub()];
|
|
const expectedAssertion: NonEmptyCollectionAssertion = {
|
|
value: data,
|
|
valueName: 'collections',
|
|
};
|
|
const validator = new TypeValidatorStub();
|
|
const sut = new ApplicationParserBuilder()
|
|
.withCollectionsData(data)
|
|
.withTypeValidator(validator);
|
|
// act
|
|
sut.parseApplication();
|
|
// assert
|
|
validator.expectNonEmptyCollectionAssertion(expectedAssertion);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
class ApplicationParserBuilder {
|
|
private categoryCollectionParser
|
|
: CategoryCollectionParser = new CategoryCollectionParserStub().getStub();
|
|
|
|
private projectDetailsParser
|
|
: typeof parseProjectDetails = new ProjectDetailsParserStub().getStub();
|
|
|
|
private validator: TypeValidator = new TypeValidatorStub();
|
|
|
|
private collectionsData: readonly CollectionData[] | undefined = [new CollectionDataStub()];
|
|
|
|
public withCategoryCollectionParser(
|
|
categoryCollectionParser: CategoryCollectionParser,
|
|
): this {
|
|
this.categoryCollectionParser = categoryCollectionParser;
|
|
return this;
|
|
}
|
|
|
|
public withProjectDetailsParser(
|
|
projectDetailsParser: typeof parseProjectDetails,
|
|
): this {
|
|
this.projectDetailsParser = projectDetailsParser;
|
|
return this;
|
|
}
|
|
|
|
public withCollectionsData(collectionsData: readonly CollectionData[] | undefined): this {
|
|
this.collectionsData = collectionsData;
|
|
return this;
|
|
}
|
|
|
|
public withTypeValidator(validator: TypeValidator): this {
|
|
this.validator = validator;
|
|
return this;
|
|
}
|
|
|
|
public parseApplication(): ReturnType<typeof parseApplication> {
|
|
return parseApplication(
|
|
this.collectionsData,
|
|
{
|
|
parseCategoryCollection: this.categoryCollectionParser,
|
|
parseProjectDetails: this.projectDetailsParser,
|
|
validator: this.validator,
|
|
},
|
|
);
|
|
}
|
|
}
|