Skip to content

Commit

Permalink
Completed verification stage
Browse files Browse the repository at this point in the history
  • Loading branch information
jezzsantos committed Jan 3, 2025
1 parent a694a91 commit dcb4a28
Show file tree
Hide file tree
Showing 10 changed files with 618 additions and 76 deletions.
4 changes: 4 additions & 0 deletions src/SaaStack.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,7 @@ public void When$condition$_Then$outcome$()
<s:Boolean x:Key="/Default/UserDictionary/Words/=afeature/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=afeaturelevel/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=afieldname/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=afile/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=afilename/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=afilter/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=afilterfield/@EntryIndexedValue">True</s:Boolean>
Expand Down Expand Up @@ -1735,6 +1736,7 @@ public void When$condition$_Then$outcome$()
<s:Boolean x:Key="/Default/UserDictionary/Words/=aregistereduserid/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=areplacement/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=arequestid/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=arequired/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=aresource/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=aresourceid/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=aresourceref/@EntryIndexedValue">True</s:Boolean>
Expand Down Expand Up @@ -1819,6 +1821,7 @@ public void When$condition$_Then$outcome$()
<s:Boolean x:Key="/Default/UserDictionary/Words/=Dtos/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=el_0022_002C_0020Y/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=emaildelivery/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=emptyjson/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Equals_0028_0022adif/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=eventstore/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=eventstoredb/@EntryIndexedValue">True</s:Boolean>
Expand All @@ -1832,6 +1835,7 @@ public void When$condition$_Then$outcome$()
<s:Boolean x:Key="/Default/UserDictionary/Words/=Hydratable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ies/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=inheritdoc/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=invalidjson/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=jsondata/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=kurrent/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=maxio/@EntryIndexedValue">True</s:Boolean>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {AppSettingsJsonFileReader} from "./appSettingsJsonFileReader";

describe('AppSettingsJsonFileReader', () => {

it('should throw, when file does not exist', async () => {

const reader = new AppSettingsJsonFileReader();

try {
await reader.readAppSettingsFile('nonexistent.json');
} catch (error) {
expect(error.message).toMatch("File 'nonexistent.json' cannot be read from disk, possibly it does not exist, or is not accessible?");
}
});

it('should throw, when file is not JSON', async () => {

const reader = new AppSettingsJsonFileReader();
const path = `${__dirname}/testing/__data/invalidjson.txt`;

try {
await reader.readAppSettingsFile(path);
} catch (error) {
expect(error.message).toMatch(`File '${path}' does not contain valid JSON: SyntaxError: Unexpected token 'i', \"invalid\" is not valid JSON`);
}
});

it('should return file, when file has no variables', async () => {

const reader = new AppSettingsJsonFileReader();
const path = `${__dirname}/testing/__data/emptyjson.json`;

const file = await reader.readAppSettingsFile(path);

expect(file).toEqual({});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import fs from "node:fs";

export interface IAppSettingsJsonFileReader {
readAppSettingsFile(path: string): Promise<any>;
}

export class AppSettingsJsonFileReader implements IAppSettingsJsonFileReader {
async readAppSettingsFile(path: string): Promise<any> {
let data: any;
try {
const result = await fs.promises.readFile(path);
data = Buffer.from(result);
} catch (error) {
throw new Error(`File '${path}' cannot be read from disk, possibly it does not exist, or is not accessible?`);
}

const raw = data.toString();
try {
return JSON.parse(raw);
} catch (error) {
throw new Error(`File '${path}' does not contain valid JSON: ${error}`);
}
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {ConfigurationSets} from "./configurationSets";
import {ILogger} from "./logger";
import {IGlobPatternParser} from "./globPatternParser";
import {IAppSettingsJsonFileReader} from "./appSettingsJsonFileReader";

describe('ConfigurationSets', () => {
const logger: jest.Mocked<ILogger> = {
Expand All @@ -9,67 +10,226 @@ describe('ConfigurationSets', () => {
error: jest.fn(),
};

it('should warn and be empty when constructed with no files', async () => {
describe('create', () => {

it('should warn and be empty when constructed with no files', async () => {

const globParser: jest.Mocked<IGlobPatternParser> = {
parseFiles: jest.fn(_matches => Promise.resolve([])),
};
const jsonFileReader: jest.Mocked<IAppSettingsJsonFileReader> = {
readAppSettingsFile: jest.fn()
};

const sets = await ConfigurationSets.create(logger, globParser, jsonFileReader, '');

expect(sets.hasNone).toBe(true);
expect(globParser.parseFiles).toHaveBeenCalledWith([]);
expect(jsonFileReader.readAppSettingsFile).not.toHaveBeenCalled();
expect(logger.warning).toHaveBeenCalledWith('No settings files found in this repository, using the glob patterns: ');
});

it('should create a single set, when has one file at the root', async () => {

const globParser: jest.Mocked<IGlobPatternParser> = {
parseFiles: jest.fn(_matches => Promise.resolve(["afile.json"])),
};
const jsonFileReader: jest.Mocked<IAppSettingsJsonFileReader> = {
readAppSettingsFile: jest.fn(_path => Promise.resolve({
"aname": "avalue"
})),
};


const sets = await ConfigurationSets.create(logger, globParser, jsonFileReader, '');

expect(sets.hasNone).toBe(false);
expect(sets.length).toBe(1);
expect(sets.sets[0].hostProjectPath).toEqual(".");
expect(sets.sets[0].settingFiles.length).toEqual(1);
expect(sets.sets[0].settingFiles[0].path).toEqual("afile.json");
expect(sets.sets[0].definedVariables).toEqual(["aname"]);
expect(sets.sets[0].requiredVariables).toEqual([]);
expect(globParser.parseFiles).toHaveBeenCalledWith([]);
expect(jsonFileReader.readAppSettingsFile).toHaveBeenCalledWith("afile.json");
expect(logger.info).toHaveBeenCalledWith('Found settings files, in these hosts:\n\t.:\n\t\tafile.json');
});

it('should create a single set, when has one file in a directory', async () => {

const globParser: jest.Mocked<IGlobPatternParser> = {
parseFiles: jest.fn(_matches => Promise.resolve(["apath/afile.json"])),
};
const jsonFileReader: jest.Mocked<IAppSettingsJsonFileReader> = {
readAppSettingsFile: jest.fn(_path => Promise.resolve({
"aname": "avalue"
})),
};

const sets = await ConfigurationSets.create(logger, globParser, jsonFileReader, '');

expect(sets.hasNone).toBe(false);
expect(sets.length).toBe(1);
expect(sets.sets[0].hostProjectPath).toEqual("apath");
expect(sets.sets[0].settingFiles.length).toEqual(1);
expect(sets.sets[0].settingFiles[0].path).toEqual("apath/afile.json");
expect(sets.sets[0].definedVariables).toEqual(["aname"]);
expect(sets.sets[0].requiredVariables).toEqual([]);
expect(globParser.parseFiles).toHaveBeenCalledWith([]);
expect(jsonFileReader.readAppSettingsFile).toHaveBeenCalledWith("apath/afile.json");
expect(logger.info).toHaveBeenCalledWith('Found settings files, in these hosts:\n\tapath:\n\t\tafile.json');
});

it('should create a single set, when has many files in same directory', async () => {

const globParser: jest.Mocked<IGlobPatternParser> = {
parseFiles: jest.fn(_matches => Promise.resolve(["apath/afile1.json", "apath/afile2.json"])),
};
const jsonFileReader: jest.Mocked<IAppSettingsJsonFileReader> = {
readAppSettingsFile: jest.fn()
};
jsonFileReader.readAppSettingsFile
.mockResolvedValueOnce({
"aname1": "avalue"
})
.mockResolvedValueOnce({
"aname2": "avalue"
});

const sets = await ConfigurationSets.create(logger, globParser, jsonFileReader, '');

expect(sets.hasNone).toBe(false);
expect(sets.length).toBe(1);
expect(sets.sets[0].hostProjectPath).toEqual("apath");
expect(sets.sets[0].settingFiles.length).toEqual(2);
expect(sets.sets[0].settingFiles[0].path).toEqual("apath/afile1.json");
expect(sets.sets[0].settingFiles[1].path).toEqual("apath/afile2.json");
expect(sets.sets[0].definedVariables).toEqual(["aname1", "aname2"]);
expect(sets.sets[0].requiredVariables).toEqual([]);
expect(globParser.parseFiles).toHaveBeenCalledWith([]);
expect(jsonFileReader.readAppSettingsFile).toHaveBeenCalledWith("apath/afile1.json");
expect(jsonFileReader.readAppSettingsFile).toHaveBeenCalledWith("apath/afile2.json");
expect(logger.info).toHaveBeenCalledWith('Found settings files, in these hosts:\n\tapath:\n\t\tafile1.json,\n\t\tafile2.json');
});

it('should create a single set with combined required variables, when both files have Required settings', async () => {

const globParser: jest.Mocked<IGlobPatternParser> = {
parseFiles: jest.fn(_matches => Promise.resolve(["apath/afile1.json", "apath/afile2.json"])),
};
const jsonFileReader: jest.Mocked<IAppSettingsJsonFileReader> = {
readAppSettingsFile: jest.fn()
};
jsonFileReader.readAppSettingsFile
.mockResolvedValueOnce({
"aname1": "avalue",
"Required": ["arequired1", "arequired2"]
})
.mockResolvedValueOnce({
"aname2": "avalue",
"Required": ["arequired2", "arequired3"]
});

const sets = await ConfigurationSets.create(logger, globParser, jsonFileReader, '');

expect(sets.hasNone).toBe(false);
expect(sets.length).toBe(1);
expect(sets.sets[0].hostProjectPath).toEqual("apath");
expect(sets.sets[0].settingFiles.length).toEqual(2);
expect(sets.sets[0].settingFiles[0].path).toEqual("apath/afile1.json");
expect(sets.sets[0].settingFiles[1].path).toEqual("apath/afile2.json");
expect(sets.sets[0].definedVariables).toEqual(["aname1", "aname2"]);
expect(sets.sets[0].requiredVariables).toEqual(["arequired1", "arequired2", "arequired3"]);
expect(globParser.parseFiles).toHaveBeenCalledWith([]);
expect(jsonFileReader.readAppSettingsFile).toHaveBeenCalledWith("apath/afile1.json");
expect(jsonFileReader.readAppSettingsFile).toHaveBeenCalledWith("apath/afile2.json");
expect(logger.info).toHaveBeenCalledWith('Found settings files, in these hosts:\n\tapath:\n\t\tafile1.json,\n\t\tafile2.json');
});
});

const globParser: jest.Mocked<IGlobPatternParser> = {
parseFiles: jest.fn(matches => Promise.resolve([])),
};
describe('verifyConfiguration', () => {
it('should return true, when there are no sets', async () => {

const sets = await ConfigurationSets.create(logger, globParser, '');
const globParser: jest.Mocked<IGlobPatternParser> = {
parseFiles: jest.fn(_matches => Promise.resolve([])),
};
const jsonFileReader: jest.Mocked<IAppSettingsJsonFileReader> = {
readAppSettingsFile: jest.fn()
};

expect(sets.hasNone).toBe(true);
expect(globParser.parseFiles).toHaveBeenCalledWith([]);
expect(logger.warning).toHaveBeenCalledWith('No settings files found in this repository, using the glob patterns: ');
});
const sets = await ConfigurationSets.create(logger, globParser, jsonFileReader, '');

it('should create a single set, when has one file at the root', async () => {
const result = sets.verifyConfiguration();

const globParser: jest.Mocked<IGlobPatternParser> = {
parseFiles: jest.fn(matches => Promise.resolve(["afile.json"])),
};
expect(result).toBe(true)
});

const sets = await ConfigurationSets.create(logger, globParser, '');
it('should return true, when the set contains no required', async () => {

expect(sets.hasNone).toBe(false);
expect(sets.length).toBe(1);
expect(sets.sets[0].HostProjectPath).toEqual(".");
expect(sets.sets[0].SettingFiles).toEqual(["afile.json"]);
expect(sets.sets[0].RequiredVariables).toEqual([]);
expect(globParser.parseFiles).toHaveBeenCalledWith([]);
expect(logger.info).toHaveBeenCalledWith('Found settings files:\n\t.:\n\t\tafile.json');
});
const globParser: jest.Mocked<IGlobPatternParser> = {
parseFiles: jest.fn(_matches => Promise.resolve(["apath/afile1.json"])),
};
const jsonFileReader: jest.Mocked<IAppSettingsJsonFileReader> = {
readAppSettingsFile: jest.fn()
};
jsonFileReader.readAppSettingsFile
.mockResolvedValueOnce({
"aname": "avalue"
});

it('should create a single set, when has one file in a directory', async () => {
const sets = await ConfigurationSets.create(logger, globParser, jsonFileReader, '');

const globParser: jest.Mocked<IGlobPatternParser> = {
parseFiles: jest.fn(matches => Promise.resolve(["apath/afile.json"])),
};
const result = sets.verifyConfiguration();

const sets = await ConfigurationSets.create(logger, globParser, '');
expect(result).toBe(true);
expect(logger.info).toHaveBeenCalledWith(`Verification of host 'apath' completed successfully`);
});

expect(sets.hasNone).toBe(false);
expect(sets.length).toBe(1);
expect(sets.sets[0].HostProjectPath).toEqual("apath");
expect(sets.sets[0].SettingFiles).toEqual(["apath/afile.json"]);
expect(sets.sets[0].RequiredVariables).toEqual([]);
expect(globParser.parseFiles).toHaveBeenCalledWith([]);
expect(logger.info).toHaveBeenCalledWith('Found settings files:\n\tapath:\n\t\tafile.json');
});
it('should return false, when the set contains required variable, and not exists', async () => {

const globParser: jest.Mocked<IGlobPatternParser> = {
parseFiles: jest.fn(_matches => Promise.resolve(["apath/afile1.json"])),
};
const jsonFileReader: jest.Mocked<IAppSettingsJsonFileReader> = {
readAppSettingsFile: jest.fn()
};
jsonFileReader.readAppSettingsFile
.mockResolvedValueOnce({
"aname": "avalue",
"Required": ["arequired"]
});

const sets = await ConfigurationSets.create(logger, globParser, jsonFileReader, '');

const result = sets.verifyConfiguration();

expect(result).toBe(false);
expect(logger.info).not.toHaveBeenCalledWith(`Verification of host 'apath' completed successfully`);
expect(logger.error).toHaveBeenCalledWith(`Required variable 'arequired' is not defined in any of the settings files of this host!`);
expect(logger.error).toHaveBeenCalledWith(`Verification of host 'apath' failed, there is at least one missing required variable!`);
});

it('should return true, when the set contains required variable, and exists', async () => {

const globParser: jest.Mocked<IGlobPatternParser> = {
parseFiles: jest.fn(_matches => Promise.resolve(["apath/afile1.json"])),
};
const jsonFileReader: jest.Mocked<IAppSettingsJsonFileReader> = {
readAppSettingsFile: jest.fn()
};
jsonFileReader.readAppSettingsFile
.mockResolvedValueOnce({
"aname": "avalue",
"Required": ["aname"]
});

it('should create a single set, when has many files in same directory', async () => {
const sets = await ConfigurationSets.create(logger, globParser, jsonFileReader, '');

const globParser: jest.Mocked<IGlobPatternParser> = {
parseFiles: jest.fn(matches => Promise.resolve(["apath/afile1.json", "apath/afile2.json"])),
};
const result = sets.verifyConfiguration();

const sets = await ConfigurationSets.create(logger, globParser, '');
expect(result).toBe(true);
expect(logger.info).toHaveBeenCalledWith(`Verification of host 'apath' completed successfully`);
});

expect(sets.hasNone).toBe(false);
expect(sets.length).toBe(1);
expect(sets.sets[0].HostProjectPath).toEqual("apath");
expect(sets.sets[0].SettingFiles).toEqual(["apath/afile1.json", "apath/afile2.json"]);
expect(sets.sets[0].RequiredVariables).toEqual([]);
expect(globParser.parseFiles).toHaveBeenCalledWith([]);
expect(logger.info).toHaveBeenCalledWith('Found settings files:\n\tapath:\n\t\tafile1.json,\n\t\tafile2.json');
});
});
Loading

0 comments on commit dcb4a28

Please sign in to comment.