Implement input validation and supporting tests with automation of the tests via actions.
This commit is contained in:
parent
d20d981190
commit
c54e517700
25
.gitea/workflows/run_tests.yaml
Normal file
25
.gitea/workflows/run_tests.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
name: Run Jest tests
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
run-tests:
|
||||
runs-on: ubuntu-22.04
|
||||
container:
|
||||
image: ghcr.io/catthehacker/ubuntu:act-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup up Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Run Tests
|
||||
run: npm test
|
6
jest.config.js
Normal file
6
jest.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
transform: {
|
||||
"^.+\\.tsx?$": ["ts-jest", { useESM: true }]
|
||||
},
|
||||
extensionsToTreatAsEsm: [".ts"],
|
||||
};
|
3833
package-lock.json
generated
3833
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -3,8 +3,9 @@
|
||||
"version": "0.0.1",
|
||||
"description": "TypeScript client for the OwlBoard API",
|
||||
"main": "index.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "jest"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -13,6 +14,12 @@
|
||||
"author": "Frederick Boniface",
|
||||
"license": "GPL-3.0",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.14",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.2.6",
|
||||
"typescript": "^5.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": "^11.1.0"
|
||||
}
|
||||
}
|
||||
|
110
src/inputValidation/inputValidation.test.ts
Normal file
110
src/inputValidation/inputValidation.test.ts
Normal file
@ -0,0 +1,110 @@
|
||||
|
||||
import { validateCrs,
|
||||
validatePisCode,
|
||||
validateReasonCode,
|
||||
validateTiploc,
|
||||
validateUuid } from "./inputValidation";
|
||||
import { ValidationError } from "../errors";
|
||||
|
||||
describe("PIS Validation Tests", () => {
|
||||
test("PIS inputs of the correct format should return true", () => {
|
||||
expect(validatePisCode(1234)).toBe(true);
|
||||
expect(validatePisCode("0999")).toBe(true);
|
||||
expect(validatePisCode(9343)).toBe(true);
|
||||
})
|
||||
|
||||
test("PIS inputs of an invalid format should throw a ValidationError", () => {
|
||||
expect(() => validatePisCode(21)).toThrow(ValidationError);
|
||||
expect(() => validatePisCode("hoo")).toThrow(ValidationError);
|
||||
expect(() => validatePisCode({test: "Value"})).toThrow(ValidationError);
|
||||
})
|
||||
})
|
||||
|
||||
describe("TIPLOC Validation Tests", () => {
|
||||
test("TIPLOC inputs of the correct format should return true", () => {
|
||||
expect(validateTiploc("elyy")).toBe(true);
|
||||
expect(validateTiploc("STROUD")).toBe(true);
|
||||
expect(validateTiploc("GLOSTER")).toBe(true);
|
||||
expect(validateTiploc("PaDtOn")).toBe(true);
|
||||
})
|
||||
|
||||
test("TIPLOC inputs of an invalid format should throw a ValidationError", () => {
|
||||
expect(() => validateTiploc("wey")).toThrow(ValidationError);
|
||||
expect(() => validateTiploc("PMS")).toThrow(ValidationError);
|
||||
expect(() => validateTiploc(89)).toThrow(ValidationError);
|
||||
expect(() => validateTiploc(["STROUD", "GLOSTER"])).toThrow(ValidationError);
|
||||
})
|
||||
})
|
||||
|
||||
describe("CRS Validation Tests", () => {
|
||||
test("CRS inputs of the correct format should return true", () => {
|
||||
expect(validateCrs("bth")).toBe(true);
|
||||
expect(validateCrs("BTH")).toBe(true);
|
||||
expect(validateCrs("PoA")).toBe(true);
|
||||
});
|
||||
|
||||
test("CRS inputs of an invalid format should throw a ValidationError", () => {
|
||||
expect(() => validateCrs("BATHSPA")).toThrow(ValidationError);
|
||||
expect(() => validateCrs("popo")).toThrow(ValidationError);
|
||||
expect(() => validateCrs("BT")).toThrow(ValidationError);
|
||||
})
|
||||
|
||||
test("CRS inputs that are not strings should throw ValidationError", () => {
|
||||
expect(() => validateCrs(34)).toThrow(ValidationError);
|
||||
expect(() => validateCrs([])).toThrow(ValidationError);
|
||||
expect(() => validateCrs({})).toThrow(ValidationError);
|
||||
expect(() => validateCrs(null)).toThrow(ValidationError);
|
||||
expect(() => validateCrs(undefined)).toThrow(ValidationError);
|
||||
})
|
||||
})
|
||||
|
||||
describe("UUID Validation Tests", () => {
|
||||
test("UUID inputs that are valid v4-UUIDs should return true", () => {
|
||||
expect(validateUuid("5c5db50b-91c8-440c-80af-afc7bb375b4b")).toBe(true);
|
||||
expect(validateUuid("9a9a3849-bbcb-452c-a0db-599a53cb27f3")).toBe(true);
|
||||
})
|
||||
|
||||
test("UUID inputs that are valid v1-UUIDs should return true", () => {
|
||||
expect(validateUuid("e7dd36f0-ff86-11ef-9cd2-0242ac120002")).toBe(true);
|
||||
expect(validateUuid("e052e8b6-ff87-11ef-9cd2-0242ac120002")).toBe(true);
|
||||
})
|
||||
|
||||
test("UUID inputs that are valid v7-UUIDs should return true", () => {
|
||||
expect(validateUuid("01958c36-8b96-7ff1-be2c-d1488273f5e0")).toBe(true);
|
||||
expect(validateUuid("01958c3a-f9d0-7c6a-b510-6de0dd36a49a")).toBe(true);
|
||||
})
|
||||
|
||||
test("UUID inputs that are a nil UUID should return true", () => {
|
||||
expect(validateUuid("00000000-0000-0000-0000-000000000000")).toBe(true);
|
||||
})
|
||||
|
||||
test("UUID inputs that are actually not UUIDs should throw ValidationError", () => {
|
||||
expect(() => validateUuid("thisisjustastring")).toThrow(ValidationError);
|
||||
expect(() => validateUuid(789)).toThrow(ValidationError);
|
||||
})
|
||||
})
|
||||
|
||||
describe("Reason Code Validation Tests", () => {
|
||||
test("Reason codes that are valid numbers or strings should return true", () => {
|
||||
expect(validateReasonCode("999")).toBe(true);
|
||||
expect(validateReasonCode("100")).toBe(true);
|
||||
expect(validateReasonCode(402)).toBe(true);
|
||||
expect(validateReasonCode(100)).toBe(true);
|
||||
expect(validateReasonCode(999)).toBe(true);
|
||||
})
|
||||
|
||||
test("Reason codes that are out of range numbers should throw ValidationError", () => {
|
||||
expect(() => validateReasonCode(99)).toThrow(ValidationError);
|
||||
expect(() => validateReasonCode("1000")).toThrow(ValidationError);
|
||||
expect(() => validateReasonCode(1000)).toThrow(ValidationError);
|
||||
expect(() => validateReasonCode("99")).toThrow(ValidationError);
|
||||
})
|
||||
|
||||
test("Reason code inputs that are non numeric values should throw ValidationError", () => {
|
||||
expect(() => validateReasonCode("19f")).toThrow(ValidationError);
|
||||
expect(() => validateReasonCode(null)).toThrow(ValidationError);
|
||||
expect(() => validateReasonCode(true)).toThrow(ValidationError);
|
||||
expect(() => validateReasonCode([])).toThrow(ValidationError);
|
||||
expect(() => validateReasonCode({})).toThrow(ValidationError);
|
||||
})
|
||||
})
|
@ -1,6 +1,13 @@
|
||||
import { ValidationError } from "../errors";
|
||||
import { validate as uuidlibValidate, version as uuidlibVersion } from 'uuid'
|
||||
|
||||
export function validatePisCode(code: string): boolean {
|
||||
export function validatePisCode(code: unknown): boolean {
|
||||
if (typeof code == "number") {
|
||||
code = code.toString();
|
||||
}
|
||||
if (typeof code !== "string") {
|
||||
throw new ValidationError("Invalid input: code must be a four digit number or string")
|
||||
}
|
||||
const codeRegex = /^\d{4}$/;
|
||||
if (!codeRegex.test(code)) {
|
||||
throw new ValidationError("Invalid input: code must be a four-character string consisting of only numerals");
|
||||
@ -8,18 +15,56 @@ export function validatePisCode(code: string): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validateTiploc(tiploc: string): boolean {
|
||||
const tiplocRegex = /[a-zA-z0-9]{4,7}/;
|
||||
export function validateTiploc(tiploc: unknown): boolean {
|
||||
if (typeof tiploc !== "string") {
|
||||
throw new ValidationError("Invalid input: TIPLOC must be a string");
|
||||
}
|
||||
const tiplocRegex = /[a-zA-Z0-9]{4,7}/;
|
||||
if (!tiplocRegex.test(tiploc)) {
|
||||
throw new ValidationError("Invalid input: TIPLOC must be between four and seven characters");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validateCrs(crs: string): boolean {
|
||||
const crsRegex = /[a-zA-z]{3}/;
|
||||
export function validateCrs(crs: unknown): boolean {
|
||||
if (typeof crs !== "string") {
|
||||
throw new ValidationError("Invalid input: CRS/3ALPHA must be a string")
|
||||
}
|
||||
|
||||
const crsRegex = /^[a-zA-Z]{3}$/;
|
||||
if (!crsRegex.test(crs)) {
|
||||
throw new ValidationError("Invalid input: CRS/3ALPHA must be exactly three letters")
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validateUuid(uuid: unknown): boolean {
|
||||
if (typeof uuid !== "string") {
|
||||
throw new ValidationError("Invalid input: The UUID/api_key should be a string");
|
||||
}
|
||||
if (!uuidlibValidate(uuid)) {
|
||||
throw new ValidationError("Invalid input: The UUID/api_key is the expexted value")
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function validateReasonCode(code: unknown): boolean {
|
||||
if (typeof code === "number") {
|
||||
// Ensure it's a 3-digit number (100-999)
|
||||
if (!Number.isInteger(code) || code < 100 || code > 999) {
|
||||
throw new ValidationError("Invalid input: Reason code must be a three-digit number (100-999).");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof code === "string") {
|
||||
// Ensure it consists of exactly 3 numeric characters
|
||||
if (!/^\d{3}$/.test(code)) {
|
||||
throw new ValidationError("Invalid input: Reason code must be a string of three digits.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// If it's neither a number nor a string, throw an error
|
||||
throw new ValidationError("Invalid input: Reason code must be a number or a string of three digits.");
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user