-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Define
host
, hostname
, and origin
variable decoders
- Loading branch information
Showing
7 changed files
with
261 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import * as fc from 'fast-check'; | ||
import * as E from 'fp-ts/Either'; | ||
|
||
import { DecodeFailed } from '..'; | ||
import { Variable } from '../Variable'; | ||
|
||
import { host } from './host'; | ||
|
||
describe(host, () => { | ||
it('accepts an arbitrary host string', () => { | ||
const decoder = host(); | ||
|
||
fc.assert( | ||
fc.property( | ||
fc | ||
.tuple( | ||
fc.webUrl(), | ||
fc.nat().filter((n) => n < 65536), | ||
) | ||
.map(([url, port]) => `${new URL(url).host}:${port}`), | ||
(str) => { | ||
const variable = new Variable('KEY', str); | ||
expect(decoder(variable)).toStrictEqual(E.right(str)); | ||
}, | ||
), | ||
); | ||
}); | ||
|
||
it('requires a port number', () => { | ||
const decoder = host(); | ||
|
||
fc.assert( | ||
fc.property( | ||
fc.webUrl().map((url) => new URL(url).hostname), | ||
(str) => { | ||
const variable = new Variable('KEY', str); | ||
expect(decoder(variable)).toStrictEqual(E.left(new DecodeFailed(variable, 'must be a valid host'))); | ||
}, | ||
), | ||
); | ||
}); | ||
|
||
it('rejects an arbitrary URL string', () => { | ||
const decoder = host(); | ||
|
||
fc.assert( | ||
fc.property( | ||
fc.webUrl().map((str) => { | ||
const url = new URL(str); | ||
url.port = '80'; | ||
return url.href; | ||
}), | ||
(str) => { | ||
const variable = new Variable('KEY', str); | ||
expect(decoder(variable)).toStrictEqual(E.left(new DecodeFailed(variable, 'must be a valid host'))); | ||
}, | ||
), | ||
); | ||
}); | ||
|
||
it('rejects the trailing pathname', () => { | ||
const decoder = host(); | ||
|
||
fc.assert( | ||
fc.property( | ||
fc | ||
.tuple( | ||
fc.webUrl().filter((url) => new URL(url).pathname !== '/'), | ||
fc.nat().filter((n) => n < 65536), | ||
) | ||
|
||
.map(([orig, port]) => { | ||
const url = new URL(orig); | ||
url.port = String(port); | ||
return url.host + url.pathname; | ||
}), | ||
(str) => { | ||
const variable = new Variable('KEY', str); | ||
expect(decoder(variable)).toStrictEqual(E.left(new DecodeFailed(variable, 'must be a valid host'))); | ||
}, | ||
), | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { pipe } from 'fp-ts/function'; | ||
import * as O from 'fp-ts/Option'; | ||
import * as RE from 'fp-ts/ReaderEither'; | ||
|
||
import { ask, decodeFailed, validate, VariableDecoder } from './VariableDecoder'; | ||
|
||
/** | ||
* Decodes a host. | ||
* A host consists of a hostname and a port number. | ||
*/ | ||
const host = (): VariableDecoder<string> => | ||
pipe( | ||
RE.Do, | ||
RE.bind('value', () => ask()), | ||
RE.bind('url', ({ value }) => | ||
pipe( | ||
O.tryCatch(() => RE.of(new URL(`https://${value}/`))), | ||
O.getOrElse(() => decodeFailed('must be a valid host')), | ||
), | ||
), | ||
RE.chain(validate(({ url }) => url.port !== '', 'must be a valid host')), | ||
RE.chain(validate(({ value, url }) => url.host === value, 'must be a valid host')), | ||
RE.map(({ value }) => value), | ||
); | ||
|
||
export { host }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import * as fc from 'fast-check'; | ||
import * as E from 'fp-ts/Either'; | ||
|
||
import { DecodeFailed } from '..'; | ||
import { Variable } from '../Variable'; | ||
|
||
import { hostname } from './hostname'; | ||
|
||
describe(hostname, () => { | ||
it('accepts an arbitrary URL string', () => { | ||
const decoder = hostname(); | ||
|
||
fc.assert( | ||
fc.property( | ||
fc.webUrl().map((url) => new URL(url).hostname), | ||
(str) => { | ||
const variable = new Variable('KEY', str); | ||
expect(decoder(variable)).toStrictEqual(E.right(str)); | ||
}, | ||
), | ||
); | ||
}); | ||
|
||
it('rejects an arbitrary host string with a port number', () => { | ||
const decoder = hostname(); | ||
|
||
fc.assert( | ||
fc.property( | ||
fc | ||
.tuple( | ||
fc.webUrl(), | ||
fc.nat().filter((n) => n < 65536), | ||
) | ||
.map(([url, port]) => `${new URL(url).hostname}:${port}`), | ||
(str) => { | ||
const variable = new Variable('KEY', str); | ||
expect(decoder(variable)).toStrictEqual( | ||
E.left(new DecodeFailed(variable, 'must be a valid hostname')), | ||
); | ||
}, | ||
), | ||
); | ||
}); | ||
|
||
it('rejects an arbitrary URL string', () => { | ||
const decoder = hostname(); | ||
|
||
fc.assert( | ||
fc.property(fc.webUrl(), (str) => { | ||
const variable = new Variable('KEY', str); | ||
expect(decoder(variable)).toStrictEqual(E.left(new DecodeFailed(variable, 'must be a valid hostname'))); | ||
}), | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { pipe } from 'fp-ts/function'; | ||
import * as O from 'fp-ts/Option'; | ||
import * as RE from 'fp-ts/ReaderEither'; | ||
|
||
import { ask, decodeFailed, validate, VariableDecoder } from './VariableDecoder'; | ||
|
||
/** | ||
* Decodes a hostname. | ||
*/ | ||
const hostname = (): VariableDecoder<string> => | ||
pipe( | ||
RE.Do, | ||
RE.bind('value', () => ask()), | ||
RE.bind('url', ({ value }) => | ||
pipe( | ||
O.tryCatch(() => RE.of(new URL(`https://${value}:80/`))), | ||
O.getOrElse(() => decodeFailed('must be a valid hostname')), | ||
), | ||
), | ||
RE.chain(validate(({ value, url }) => url.hostname === value, 'must be a valid hostname')), | ||
RE.map(({ value }) => value), | ||
); | ||
|
||
export { hostname }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import * as fc from 'fast-check'; | ||
import * as E from 'fp-ts/Either'; | ||
|
||
import { DecodeFailed } from '..'; | ||
import { Variable } from '../Variable'; | ||
|
||
import { origin } from './origin'; | ||
|
||
describe(origin, () => { | ||
it('accepts an arbitrary origin string', () => { | ||
const decoder = origin(); | ||
|
||
fc.assert( | ||
fc.property( | ||
fc | ||
.tuple( | ||
fc.webUrl(), | ||
fc.nat().filter((n) => n < 65536), | ||
) | ||
.map(([url, port]) => `${new URL(url).origin}:${port}`), | ||
(str) => { | ||
const variable = new Variable('KEY', str); | ||
expect(decoder(variable)).toStrictEqual(E.right(str)); | ||
}, | ||
), | ||
); | ||
}); | ||
|
||
it('rejects the complete URL', () => { | ||
const decoder = origin(); | ||
|
||
fc.assert( | ||
fc.property( | ||
fc.webUrl().filter((url) => new URL(url).pathname !== '/'), | ||
(str) => { | ||
const variable = new Variable('KEY', str); | ||
expect(decoder(variable)).toStrictEqual( | ||
E.left(new DecodeFailed(variable, 'must be a valid URL origin')), | ||
); | ||
}, | ||
), | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { pipe } from 'fp-ts/function'; | ||
import * as O from 'fp-ts/Option'; | ||
import * as RE from 'fp-ts/ReaderEither'; | ||
|
||
import { ask, decodeFailed, validate, VariableDecoder } from './VariableDecoder'; | ||
|
||
/** | ||
* Decodes a URL origin (scheme + hostname + port). | ||
*/ | ||
const origin = (): VariableDecoder<string> => | ||
pipe( | ||
RE.Do, | ||
RE.bind('value', () => ask()), | ||
RE.bind('url', ({ value }) => | ||
pipe( | ||
O.tryCatch(() => RE.of(new URL(value))), | ||
O.getOrElse(() => decodeFailed('must be a valid URL origin')), | ||
), | ||
), | ||
RE.chain(validate(({ url }) => url.pathname !== '', 'must not have a pathname')), | ||
RE.chain(validate(({ value, url }) => url.origin === value, 'must be a valid URL origin')), | ||
RE.map(({ value }) => value), | ||
); | ||
|
||
export { origin }; |