Skip to content

Commit

Permalink
Implement functions that checks the passed http status code value is …
Browse files Browse the repository at this point in the history
…in the expected range

This adds functions that checks the passed http status code value is in the expected range for `HttpStatus.***`
Names of each of functions are sorted with the spec. They uses `is<Semantics><NumberRange>` convention.

- [`HttpStatus.isInformational1xx`](https://httpwg.org/specs/rfc9110.html#status.1xx)
- [`HttpStatus.isSuccessful2xx`](https://httpwg.org/specs/rfc9110.html#status.2xx)
- [`HttpStatus.isRedirection3xx`](https://httpwg.org/specs/rfc9110.html#status.3xx)
- [`HttpStatus.isClientError4xx`](https://httpwg.org/specs/rfc9110.html#status.4xx)
- [`HttpStatus.isServerError5xx`](https://httpwg.org/specs/rfc9110.html#status.5xx)

We take an approach for unit test that is similar to
[property based testing](https://en.wikipedia.org/wiki/Software_testing#Property_testing).
The test case input values that satisfies a conditions (For http status code. It's very very simple condition).
And all test case files must check input patterns has no problem (no overlap, no missing spans) before running tests in them.
  • Loading branch information
tetsuharuohzeki committed Jul 17, 2024
1 parent 3432318 commit 4e589c5
Show file tree
Hide file tree
Showing 8 changed files with 311 additions and 0 deletions.
70 changes: 70 additions & 0 deletions packages/http-helper/src/http_status_code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,73 @@ export const HTTP_VERSION_NOT_SUPPORTED_505 = 505;
* - https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/511
*/
export const NETWORK_AUTHENTICATION_REQUIRED_511 = 511;

/**
* @param code
* The http status code.
* @returns
* This will be `true` if the _code_ is in the range of [1xx][1xx].
* Otherwise, return `false`.
*
* [1xx]: https://httpwg.org/specs/rfc9110.html#status.1xx
*/
export function isInformational1xx(code: number): boolean {
const ok = 100 <= code && code < 200;
return ok;
}

/**
* @param code
* The http status code.
* @returns
* This will be `true` if the _code_ is in the range of [2xx][2xx].
* Otherwise, return `false`.
*
* [2xx]: https://httpwg.org/specs/rfc9110.html#status.2xx
*/
export function isSuccessful2xx(code: number): boolean {
const ok = 200 <= code && code < 300;
return ok;
}

/**
* @param code
* The http status code.
* @returns
* This will be `true` if the _code_ is in the range of [3xx][3xx].
* Otherwise, return `false`.
*
* [3xx]: https://httpwg.org/specs/rfc9110.html#status.3xx
*/
export function isRedirection3xx(code: number): boolean {
const ok = 300 <= code && code < 400;
return ok;
}

/**
* @param code
* The http status code.
* @returns
* This will be `true` if the _code_ is in the range of [4xx][4xx].
* Otherwise, return `false`.
*
* [4xx]: https://httpwg.org/specs/rfc9110.html#status.4xx
*/
export function isClientError4xx(code: number): boolean {
const ok = 400 <= code && code < 500;
return ok;
}

/**
* @param code
* The http status code.
* @returns
* This will be `true` if the _code_ is in the range of [5xx][5xx].
* Otherwise, return `false`.
*
* [5xx]: https://httpwg.org/specs/rfc9110.html#status.5xx
*/
export function isServerError5xx(code: number): boolean {
const ok = 500 <= code && code < 600;
return ok;
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,10 @@ exports[`HttpStatus exported items 1`] = `
"UPGRADE_REQUIRED_426": 426,
"URI_TOO_LONG_414": 414,
"USE_PROXY_305": 305,
"isClientError4xx": [Function],
"isInformational1xx": [Function],
"isRedirection3xx": [Function],
"isServerError5xx": [Function],
"isSuccessful2xx": [Function],
}
`;
46 changes: 46 additions & 0 deletions packages/unittests/__tests__/http_status_code/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { assert } from 'vitest';

export function assertNoOverlap(a: ReadonlySet<number>, b: ReadonlySet<number>): void {
const ok = a.isDisjointFrom(b);

Check failure on line 4 in packages/unittests/__tests__/http_status_code/helper.ts

View workflow job for this annotation

GitHub Actions / ci / unittest

__tests__/http_status_code/is_client_error_4xx.test.ts

TypeError: a.isDisjointFrom is not a function ❯ Module.assertNoOverlap __tests__/http_status_code/helper.ts:4:18 ❯ __tests__/http_status_code/is_client_error_4xx.test.ts:24:5

Check failure on line 4 in packages/unittests/__tests__/http_status_code/helper.ts

View workflow job for this annotation

GitHub Actions / ci / unittest

__tests__/http_status_code/is_informational_1xx.test.ts

TypeError: a.isDisjointFrom is not a function ❯ Module.assertNoOverlap __tests__/http_status_code/helper.ts:4:18 ❯ __tests__/http_status_code/is_informational_1xx.test.ts:24:5

Check failure on line 4 in packages/unittests/__tests__/http_status_code/helper.ts

View workflow job for this annotation

GitHub Actions / ci / unittest

__tests__/http_status_code/is_redirection_3xx.test.ts

TypeError: a.isDisjointFrom is not a function ❯ Module.assertNoOverlap __tests__/http_status_code/helper.ts:4:18 ❯ __tests__/http_status_code/is_redirection_3xx.test.ts:24:5

Check failure on line 4 in packages/unittests/__tests__/http_status_code/helper.ts

View workflow job for this annotation

GitHub Actions / ci / unittest

__tests__/http_status_code/is_server_error_5xx.test.ts

TypeError: a.isDisjointFrom is not a function ❯ Module.assertNoOverlap __tests__/http_status_code/helper.ts:4:18 ❯ __tests__/http_status_code/is_server_error_5xx.test.ts:24:5

Check failure on line 4 in packages/unittests/__tests__/http_status_code/helper.ts

View workflow job for this annotation

GitHub Actions / ci / unittest

__tests__/http_status_code/is_successful_2xx.test.ts

TypeError: a.isDisjointFrom is not a function ❯ Module.assertNoOverlap __tests__/http_status_code/helper.ts:4:18 ❯ __tests__/http_status_code/is_successful_2xx.test.ts:24:5
if (!ok) {
// we want to avoid to iterate sets multiple times....
const overlapping = a.intersection(b);
assert(false, `test cases have overlap point. They are: ${Array.from(overlapping)}`);
}
}

const RANGE_BEGIN = 0;
// We believe the community does not inroduce new status code range over 999.
const RANGE_END = 999;

export function assertNoLackPatterns(...ranges: Array<ReadonlySet<number>>): void {
const base = new Set(testRange(RANGE_BEGIN, RANGE_END));
let differ = base;
for (const range of ranges) {
differ = differ.difference(range);
}
const noDifference = differ.size === 0;
if (!noDifference) {
assert(false, `There are missing spans: ${Array.from(differ)}`);
}
}

function* testRange(begin: number, end: number): Generator<number> {
assert(begin <= end);

for (let i = begin; i <= end; ++i) {
yield i;
}
}

export function* validRange(begin: number, end: number): Generator<number> {
yield* testRange(begin, end);
}

export function* invalidRangeFrom0To(end: number): Generator<number> {
yield* testRange(RANGE_BEGIN, end);
}

export function* invalidRangeToEndFrom(begin: number): Generator<number> {
yield* testRange(begin, RANGE_END);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { test, expect, beforeAll } from 'vitest';

import { HttpStatus } from '@nikkei/http-helper';
import {
assertNoLackPatterns,
assertNoOverlap,
invalidRangeFrom0To,
invalidRangeToEndFrom,
validRange,
} from './helper.js';

const TRUE_RANGE: ReadonlySet<number> = new Set([
// @prettier-ignore
...validRange(400, 499),
]);

const FALSE_RANGE: ReadonlySet<number> = new Set([
// @prettier-ignore
...invalidRangeFrom0To(399),
...invalidRangeToEndFrom(500),
]);

beforeAll(() => {
assertNoOverlap(TRUE_RANGE, FALSE_RANGE);
assertNoLackPatterns(TRUE_RANGE, FALSE_RANGE);
});

for (const input of TRUE_RANGE) {
test(`isClientError4xx(): input is \`${String(input)}\``, () => {
expect(HttpStatus.isClientError4xx(input)).toEqual(true);
});
}

for (const input of FALSE_RANGE) {
test(`isClientError4xx(): input is \`${String(input)}\``, () => {
expect(HttpStatus.isClientError4xx(input)).toEqual(false);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { test, expect, beforeAll } from 'vitest';

import { HttpStatus } from '@nikkei/http-helper';
import {
assertNoLackPatterns,
assertNoOverlap,
invalidRangeFrom0To,
invalidRangeToEndFrom,
validRange,
} from './helper.js';

const TRUE_RANGE: ReadonlySet<number> = new Set([
// @prettier-ignore
...validRange(100, 199),
]);

const FALSE_RANGE: ReadonlySet<number> = new Set([
// @prettier-ignore
...invalidRangeFrom0To(99),
...invalidRangeToEndFrom(200),
]);

beforeAll(() => {
assertNoOverlap(TRUE_RANGE, FALSE_RANGE);
assertNoLackPatterns(TRUE_RANGE, FALSE_RANGE);
});

for (const input of TRUE_RANGE) {
test(`isInformational1xx(): input is \`${String(input)}\``, () => {
expect(HttpStatus.isInformational1xx(input)).toEqual(true);
});
}

for (const input of FALSE_RANGE) {
test(`isInformational1xx(): input is \`${String(input)}\``, () => {
expect(HttpStatus.isInformational1xx(input)).toEqual(false);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { test, expect, beforeAll } from 'vitest';

import { HttpStatus } from '@nikkei/http-helper';
import {
assertNoLackPatterns,
assertNoOverlap,
invalidRangeFrom0To,
invalidRangeToEndFrom,
validRange,
} from './helper.js';

const TRUE_RANGE: ReadonlySet<number> = new Set([
// @prettier-ignore
...validRange(300, 399),
]);

const FALSE_RANGE: ReadonlySet<number> = new Set([
// @prettier-ignore
...invalidRangeFrom0To(299),
...invalidRangeToEndFrom(400),
]);

beforeAll(() => {
assertNoOverlap(TRUE_RANGE, FALSE_RANGE);
assertNoLackPatterns(TRUE_RANGE, FALSE_RANGE);
});

for (const input of TRUE_RANGE) {
test(`isRedirection3xx(): input is \`${String(input)}\``, () => {
expect(HttpStatus.isRedirection3xx(input)).toEqual(true);
});
}

for (const input of FALSE_RANGE) {
test(`isRedirection3xx(): input is \`${String(input)}\``, () => {
expect(HttpStatus.isRedirection3xx(input)).toEqual(false);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { test, expect, beforeAll } from 'vitest';

import { HttpStatus } from '@nikkei/http-helper';
import {
assertNoLackPatterns,
assertNoOverlap,
invalidRangeFrom0To,
invalidRangeToEndFrom,
validRange,
} from './helper.js';

const TRUE_RANGE: ReadonlySet<number> = new Set([
// @prettier-ignore
...validRange(500, 599),
]);

const FALSE_RANGE: ReadonlySet<number> = new Set([
// @prettier-ignore
...invalidRangeFrom0To(499),
...invalidRangeToEndFrom(600),
]);

beforeAll(() => {
assertNoOverlap(TRUE_RANGE, FALSE_RANGE);
assertNoLackPatterns(TRUE_RANGE, FALSE_RANGE);
});

for (const input of TRUE_RANGE) {
test(`isServerError5xx(): input is \`${String(input)}\``, () => {
expect(HttpStatus.isServerError5xx(input)).toEqual(true);
});
}

for (const input of FALSE_RANGE) {
test(`isServerError5xx(): input is \`${String(input)}\``, () => {
expect(HttpStatus.isServerError5xx(input)).toEqual(false);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { test, expect, beforeAll } from 'vitest';

import { HttpStatus } from '@nikkei/http-helper';
import {
assertNoLackPatterns,
assertNoOverlap,
invalidRangeFrom0To,
invalidRangeToEndFrom,
validRange,
} from './helper.js';

const TRUE_RANGE: ReadonlySet<number> = new Set([
// @prettier-ignore
...validRange(200, 299),
]);

const FALSE_RANGE: ReadonlySet<number> = new Set([
// @prettier-ignore
...invalidRangeFrom0To(199),
...invalidRangeToEndFrom(300),
]);

beforeAll(() => {
assertNoOverlap(TRUE_RANGE, FALSE_RANGE);
assertNoLackPatterns(TRUE_RANGE, FALSE_RANGE);
});

for (const input of TRUE_RANGE) {
test(`isSuccessful2xx(): input is \`${String(input)}\``, () => {
expect(HttpStatus.isSuccessful2xx(input)).toEqual(true);
});
}

for (const input of FALSE_RANGE) {
test(`isSuccessful2xx(): input is \`${String(input)}\``, () => {
expect(HttpStatus.isSuccessful2xx(input)).toEqual(false);
});
}

0 comments on commit 4e589c5

Please sign in to comment.