Skip to content

Commit 7fabf40

Browse files
author
ChadKluck
committed
updated tests
1 parent b978ba5 commit 7fabf40

File tree

9 files changed

+134
-37
lines changed

9 files changed

+134
-37
lines changed

application-infrastructure/README-Application-Structure.md

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,6 @@ All (non secret) configuration files and methods safe for repositories and used
141141

142142
Shared methods that serve as tools, helpers, and utilities can be stored in the `utils` directory. These methods should be independent of Configurations, controllers, views, and models here. As your organization develops methods that are constantly re-used, they should probably be deployed as a Lambda Layer.
143143

144-
### Tests directory
145-
146-
`src/tests/`
147-
148-
The `tests` directory can be used to store your tests written using Chai or other testing framework.
149-
150-
There is a sample-data directory in models to store data that can be used for mocking requests, or conducting tests against.
151-
152144
### Package JSON file
153145

154146
`src/package.json`
@@ -238,12 +230,40 @@ Why you might do this:
238230
- Easier to implement shared header or query string parameters (such as `format=json` if your endpoint requires such things)
239231
- Easier to provide limits, pagination, and unique error code handling for the endpoints.
240232

241-
### Static Data Directory
233+
#### Static Data Directory
242234

243-
`src/models/static/`
235+
`src/models/static-data/`
244236

245-
The `models/static` directory contains supplemental static data that can be used for mapping and enhancing data returned by your application.
237+
The `models/static-data` directory contains supplemental static data that can be used for mapping and enhancing data returned by your application.
246238

247-
Any data that isn't dynamic can be stored and used as labels, mappings, enhancements, etc.
239+
Any data that isn't dynamic can be stored and used as labels, mappings, enhancements, valid parameter lists, etc.
248240

249241
For example, suppose data from an outside system returns coded location information. For example, `US-CHI` for Chicago. You can store a JSON object with the code mappings so that your API web service can return a fully formatted, human readable location.
242+
243+
Another example is validating passed parameters. Suppose your application accepts a query string parameter for color coded data where there is a limited number of options and they stay relatively static. `['RED', 'BLUE', 'YELLOW', 'ORANGE', 'GREEN']` You store these as a separate file in static data and then import into your validation script to validate that the value passed from the client matches one of the accepted values.
244+
245+
#### Sample Data Directory
246+
247+
`src/models/sample-data`
248+
249+
The `models/sample-data` directory contains sample data that represents data is returned from Endpoints and Data Access Objects. They can be used during testing and early prototyping.
250+
251+
For example, you could create a unit test that passes the data from a sample data file into a view and compares it to the expected output contained within a test-data file (See [Test Data](#test-data-directory)).
252+
253+
#### Test Data Directory
254+
255+
`src/models/test-data`
256+
257+
The `models/test-data` directory contains test data that represents data used for testing.
258+
259+
For example, you could create a unit test that passes the data from a sample data file into a view and compares it to the expected output contained within a test-data file. (See [Sample Data](#sample-data-directory)).
260+
261+
### Tests directory
262+
263+
`src/tests/`
264+
265+
The `tests` directory can be used to store your tests written using Mocha or other testing framework.
266+
267+
You can utilize sample and test data from the models directory for mocking and comparing results.
268+
269+
For example, you can read in a sample data file (written in JSON or JavaScript) and pass it to a method. You can then compare the output using a test data file. (See [Sample Data](#sample-data-directory) and [Test Data](#test-data-directory))

application-infrastructure/src/config/validations.js

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { statusCodes } = require("../models/static/index.js");
1+
const { statusCodes } = require("../models/static-data/index.js");
22
const { tools: {DebugAndLog} } = require("@63klabs/cache-data");
33

44
const referrers = [
@@ -20,7 +20,7 @@ const isStringOfNumbers = (value) => {
2020
// used in the organizationCode route handler to validate the organizationCode parameter
2121
const statusCodePathParameter = (code) => {
2222
if (!Array.isArray(statusCodes) || statusCodes.length < 1) {
23-
DebugAndLog.error("No status codes found in the application's static data folder '/data'!");
23+
DebugAndLog.error("No status codes found in the application's static data folder '/models/static-data'!");
2424
return false;
2525
}
2626
if (!code) return false;
@@ -29,14 +29,19 @@ const statusCodePathParameter = (code) => {
2929
return statusCodes.some((statusCode) => statusCode.code === code);
3030
};
3131

32-
const idPathParameter = (gameId) => {
33-
if (!gameId) return false;
34-
if (!isString(gameId)) return false;
35-
if (!isStringOfNumbers(gameId)) return false; // demonstrates isStringOfNumbers() but next check does further inspection.
36-
// check if the gameId is a valid number between 0 and 14 (inclusive)
37-
const idNumber = parseInt(gameId, 10);
32+
const idPathParameter = (id) => {
33+
if (!id) return false;
34+
if (!isString(id)) return false;
35+
// separate id by -
36+
const [char, gid] = id.split("-");
37+
if (!char || !gid) return false;
38+
if (!isStringOfNumbers(gid)) return false;
39+
// check if char is a letter
40+
if (!char.match(/[A-Z]/)) return false;
41+
// check if number is a number within range
42+
const idNumber = parseInt(gid, 10);
3843
if (isNaN(idNumber)) return false; // not a number
39-
if (idNumber < 0 || idNumber > 14) return false;
44+
if (idNumber < 1 || idNumber > 15) return false;
4045
return true;
4146
};
4247

application-infrastructure/src/models/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const ExampleDao = require("./Example.dao");
2-
const statusCodes = require("./static/statusCodes");
2+
const statusCodes = require("./static-data/statusCodes");
33

44
module.exports = {
55
ExampleDao,
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"count": 15,
3+
"items": [
4+
{ "game_id": "G-1", "display_name": "Falken's Maze" },
5+
{ "game_id": "G-2", "display_name": "Black Jack" },
6+
{ "game_id": "G-3", "display_name": "Gin Rummy" },
7+
{ "game_id": "G-4", "display_name": "Hearts" },
8+
{ "game_id": "G-5", "display_name": "Bridge" },
9+
{ "game_id": "G-6", "display_name": "Checkers" },
10+
{ "game_id": "G-7", "display_name": "Chess" },
11+
{ "game_id": "G-8", "display_name": "Poker" },
12+
{ "game_id": "G-9", "display_name": "Fighter Combat" },
13+
{ "game_id": "G-10", "display_name": "Guerrilla Engagement" },
14+
{ "game_id": "G-11", "display_name": "Desert Warfare" },
15+
{ "game_id": "G-12", "display_name": "Air-To-Ground Actions" },
16+
{ "game_id": "G-13", "display_name": "Theaterwide Tactical Warfare" },
17+
{
18+
"game_id": "G-14",
19+
"display_name": "Theaterwide Biotoxic and Chemical Warfare"
20+
},
21+
{ "game_id": "G-15", "display_name": "Global Thermonuclear War" }
22+
]
23+
}

application-infrastructure/src/tests/index.mjs

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { readFile } from 'fs/promises';
88
import { expect } from "chai";
99

1010
import validations from '../config/validations.js';
11+
import { view as exampleView } from '../views/example.view.js';
1112

1213

1314
console.log(`Testing Against Node version ${tools.nodeVerMajor} (${tools.nodeVer})`);
@@ -37,7 +38,7 @@ describe('Test validations from config/validations.js', () => {
3738

3839
before(async () => {
3940
try {
40-
const filePath = path.join(__dirname, '..', 'models', 'static', 'statusCodes.json');
41+
const filePath = path.join(__dirname, '..', 'models', 'static-data', 'statusCodes.json');
4142
const fileContent = await readFile(filePath, 'utf8');
4243
statusCodes = JSON.parse(fileContent);
4344
} catch (error) {
@@ -82,27 +83,76 @@ describe('Test validations from config/validations.js', () => {
8283
});
8384
});
8485

85-
describe('validations.parameters.pathParameters.gamePrimaryId', () => {
86+
describe('validations.parameters.pathParameters.id', () => {
8687
it('should validate a valid game ID', () => {
87-
const gamePrimaryId = '6';
88-
expect(validations.parameters.pathParameters.id(gamePrimaryId)).to.be.true;
88+
const id = 'G-6';
89+
expect(validations.parameters.pathParameters.id(id)).to.be.true;
8990
});
9091

9192
it('should reject an invalid game ID', () => {
92-
const gamePrimaryId = 'invalid_game_id';
93-
expect(validations.parameters.pathParameters.id(gamePrimaryId)).to.be.false;
93+
const id = 'invalid_game_id';
94+
expect(validations.parameters.pathParameters.id(id)).to.be.false;
9495
});
9596

9697
it('should reject an invalid game ID based on negative value', () => {
97-
const gamePrimaryId = '-1';
98-
expect(validations.parameters.pathParameters.id(gamePrimaryId)).to.be.false;
98+
const id = '-1';
99+
expect(validations.parameters.pathParameters.id(id)).to.be.false;
99100
});
100101

101102
it('should reject an invalid game ID based on length (too long)', () => {
102-
const gamePrimaryId = '102';
103-
expect(validations.parameters.pathParameters.id(gamePrimaryId)).to.be.false;
103+
const id = '102';
104+
expect(validations.parameters.pathParameters.id(id)).to.be.false;
105+
});
106+
107+
it('should reject an invalid game ID based on length (too short)', () => {
108+
const id = 'G-';
109+
expect(validations.parameters.pathParameters.id(id)).to.be.false;
104110
});
105111

106112
})
107113
});
108114

115+
116+
/* ****************************************************************************
117+
* Views
118+
*/
119+
120+
describe('Test views/example.view.js', () => {
121+
let sampleData;
122+
let expectedData;
123+
124+
before(async () => {
125+
try {
126+
// Load input sample data
127+
const sampleFilePath = path.join(__dirname, '..', 'models', 'sample-data', 'example.dao.sample.json');
128+
const sampleFileContent = await readFile(sampleFilePath, 'utf8');
129+
sampleData = JSON.parse(sampleFileContent);
130+
131+
// Load expected output data
132+
const expectedFilePath = path.join(__dirname, '..', 'models', 'test-data', 'example.view.output.json');
133+
const expectedFileContent = await readFile(expectedFilePath, 'utf8');
134+
expectedData = JSON.parse(expectedFileContent);
135+
} catch (error) {
136+
console.error('Error loading test data files:', error);
137+
throw error;
138+
}
139+
});
140+
141+
describe('view function', () => {
142+
it('should transform example data correctly', () => {
143+
// Process the sample data through the view
144+
const result = exampleView(sampleData);
145+
146+
// Check that the structure is maintained
147+
expect(result).to.have.property('items').that.is.an('array');
148+
expect(result).to.have.property('count', expectedData.count);
149+
expect(result.items).to.have.lengthOf(expectedData.items.length);
150+
151+
// Check each example's transformed data against expected data
152+
for (let i = 0; i < expectedData.items.length; i++) {
153+
expect(result.items[i]).to.deep.include(expectedData.items[i]);
154+
}
155+
});
156+
157+
});
158+
});

application-infrastructure/src/views/example.view.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const filter = (dataItem = null) => {
1919
}
2020

2121
// For this data we want to assign an index to each item in the view, so we can track it
22-
let index = -1;
22+
let index = 0;
2323

2424
/**
2525
* Generic transformer
@@ -29,8 +29,8 @@ let index = -1;
2929
const transform = (data) => {
3030

3131
const returnData = {
32-
id: index++,
33-
display: data
32+
game_id: `G-${++index}`,
33+
display_name: data
3434
};
3535

3636
return returnData;
@@ -43,7 +43,6 @@ const transform = (data) => {
4343
*/
4444
exports.view = (resultsFromSvc) => {
4545
const viewTimer = new Timer(`Timer: ${logIdentifier}`, true);
46-
DebugAndLog.debug(`View: ${logIdentifier}`, data);
4746

4847
// if data then data else empty array
4948
const dataArray = Array.isArray(resultsFromSvc?.gamechoices) ? resultsFromSvc?.gamechoices : [];
@@ -53,7 +52,7 @@ exports.view = (resultsFromSvc) => {
5352
// which is more efficient especially for large datasets,
5453
// But for clarity we will keep them separate here.
5554

56-
// filter out the data
55+
// filter the data
5756
const filteredArray = dataArray.filter((item) => filter(item));
5857

5958
// transform the data

0 commit comments

Comments
 (0)