Skip to content

Commit c8c10da

Browse files
committed
fix: strict typecheck
1 parent 4d79ffa commit c8c10da

File tree

5 files changed

+96
-37
lines changed

5 files changed

+96
-37
lines changed

src/keys.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,16 @@ export default ({ serialize, deserialize, plans, redis } = {}) => {
3939
* @param {boolean} [options.validate=true] - Validate if the plan id is valid.
4040
* @param {boolean} [options.throwError=false] - Throw an error if the plan does not exist.
4141
*
42-
* @returns {Object} The key.
42+
* @returns {Object|null} The key object, null if it doesn't exist.
4343
*/
4444
const retrieve = async (
4545
keyId,
4646
{ throwError = false, validate = true } = {}
4747
) => {
4848
const key = await redis.get(getKey(keyId, { validate }))
49-
if (key === null && throwError) { throw new TypeError(`The key \`${keyId}\` does not exist.`) }
49+
if (key === null && throwError) {
50+
throw new TypeError(`The key \`${keyId}\` does not exist.`)
51+
}
5052
return deserialize(key)
5153
}
5254

@@ -60,16 +62,19 @@ export default ({ serialize, deserialize, plans, redis } = {}) => {
6062
const del = async keyId => {
6163
const key = await retrieve(keyId, { verify: true })
6264

63-
if (key !== null && key.plan) {
64-
const plan = await plans.retrieve(key.plan, { throwError: true, validate: false })
65+
if (key !== null && typeof key.plan === 'string') {
66+
const plan = await plans.retrieve(key.plan, {
67+
throwError: true,
68+
validate: false
69+
})
6570
if (plan !== null) {
6671
throw new TypeError(
6772
`The key \`${keyId}\` is associated with the plan \`${getKey.plan}\``
6873
)
6974
}
7075
}
7176

72-
const isDeleted = Boolean(await redis.del(getKey(keyId, { verify: true })))
77+
const isDeleted = (await redis.del(getKey(keyId, { verify: true }))) === 1
7378
if (!isDeleted) throw new TypeError(`The key \`${keyId}\` does not exist.`)
7479
return isDeleted
7580
}
@@ -89,7 +94,6 @@ export default ({ serialize, deserialize, plans, redis } = {}) => {
8994
*/
9095
const update = async (keyId, opts) => {
9196
const currentKey = await retrieve(keyId, { throwError: true })
92-
if (!currentKey) throw new TypeError(`The key \`${keyId}\` does not exist.`)
9397
const metadata = Object.assign({}, currentKey.metadata, opts.metadata)
9498
const key = Object.assign(currentKey, pick(opts, KEY_FIELDS), {
9599
updatedAt: Date.now()

src/plans.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
'use strict'
2-
31
import { pick, uid, validateKey } from './util.js'
42

53
export const PLAN_PREFIX = 'plan_'
@@ -22,7 +20,7 @@ export default ({ serialize, deserialize, redis } = {}) => {
2220
* @param {number} [options.throttle.rateLimit] - The rate limit of the plan.
2321
* @param {Object} [options.metadata] - Any extra information can be attached here.
2422
*
25-
* @returns {Object} The created plan.
23+
* @returns {Object|null} The plan object, null if it doesn't exist.
2624
*/
2725
const create = async (opts = {}) => {
2826
if (!opts.name) throw TypeError('The argument `name` is required.')
@@ -58,7 +56,9 @@ export default ({ serialize, deserialize, redis } = {}) => {
5856
{ throwError = false, validate = true } = {}
5957
) => {
6058
const plan = await redis.get(getKey(planId, { validate }))
61-
if (plan === null && throwError) { throw new TypeError(`The plan \`${planId}\` does not exist.`) }
59+
if (plan === null && throwError) {
60+
throw new TypeError(`The plan \`${planId}\` does not exist.`)
61+
}
6262
return deserialize(plan)
6363
}
6464

@@ -71,8 +71,12 @@ export default ({ serialize, deserialize, redis } = {}) => {
7171
* @returns {boolean} Whether the plan was deleted or not.
7272
*/
7373
const del = async planId => {
74-
const isDeleted = Boolean(await redis.del(getKey(planId, { validate: true })))
75-
if (!isDeleted) throw new TypeError(`The plan \`${planId}\` does not exist.`)
74+
const isDeleted = Boolean(
75+
await redis.del(getKey(planId, { validate: true }))
76+
)
77+
if (!isDeleted) {
78+
throw new TypeError(`The plan \`${planId}\` does not exist.`)
79+
}
7680
return isDeleted
7781
}
7882

src/util.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ export const validateKey =
2525
({ prefix }) =>
2626
(id, { validate = true } = {}) => {
2727
if (!validate) return id
28-
if (!id.startsWith(prefix)) throw new TypeError(`The id \`${id}\` must to start with \`${prefix}\`.`)
28+
if (!String(id).startsWith(prefix)) { throw new TypeError(`The id \`${id}\` must to start with \`${prefix}\`.`) }
2929
return id
3030
}

test/keys.js

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
'use strict'
2-
31
import { setTimeout } from 'timers/promises'
42
import openkey from 'openkey'
53
import Redis from 'ioredis'
@@ -24,8 +22,19 @@ test('.create # `name` is required', async t => {
2422
t.is(error.name, 'TypeError')
2523
})
2624

25+
test('.create # error if plan is invalid', async t => {
26+
const error = await t.throwsAsync(
27+
keys.create({ name: '[email protected]', plan: 123 })
28+
)
29+
30+
t.is(error.message, 'The id `123` must to start with `plan_`.')
31+
t.is(error.name, 'TypeError')
32+
})
33+
2734
test('.create # error if plan does not exist', async t => {
28-
const error = await t.throwsAsync(keys.create({ name: '[email protected]', plan: 'plan_123' }))
35+
const error = await t.throwsAsync(
36+
keys.create({ name: '[email protected]', plan: 'plan_123' })
37+
)
2938

3039
t.is(error.message, 'The plan `plan_123` does not exist.')
3140
t.is(error.name, 'TypeError')
@@ -55,7 +64,9 @@ test('.retrieve', async t => {
5564
})
5665

5766
test('.update', async t => {
58-
const { id, value, createdAt } = await keys.create({ name: '[email protected]' })
67+
const { id, value, createdAt } = await keys.create({
68+
69+
})
5970

6071
await setTimeout(0) // ensure time move forward
6172

@@ -77,16 +88,31 @@ test('.update', async t => {
7788
t.deepEqual(await keys.retrieve(id), { ...key, updatedAt })
7889
})
7990

80-
test('.update # error if plan does not exist', async t => {
91+
test('.update # error if plan is invalid', async t => {
8192
const { id } = await keys.create({ name: '[email protected]' })
8293

83-
await setTimeout(0) // ensure time move forward
94+
const error = await t.throwsAsync(
95+
keys.update(id, {
96+
description: 'new description',
97+
enabled: false,
98+
plan: 123
99+
})
100+
)
84101

85-
const error = await t.throwsAsync(keys.update(id, {
86-
description: 'new description',
87-
enabled: false,
88-
plan: 'plan_123'
89-
}))
102+
t.is(error.message, 'The id `123` must to start with `plan_`.')
103+
t.is(error.name, 'TypeError')
104+
})
105+
106+
test('.update # error if plan does not exist', async t => {
107+
const { id } = await keys.create({ name: '[email protected]' })
108+
109+
const error = await t.throwsAsync(
110+
keys.update(id, {
111+
description: 'new description',
112+
enabled: false,
113+
plan: 'plan_123'
114+
})
115+
)
90116

91117
t.is(error.message, 'The plan `plan_123` does not exist.')
92118
t.is(error.name, 'TypeError')
@@ -142,10 +168,17 @@ test.serial('.list', async t => {
142168
})
143169

144170
test('.del', async t => {
145-
const { id } = await keys.create({ name: '[email protected]' })
171+
{
172+
const { id } = await keys.create({ name: '[email protected]' })
146173

147-
t.true(await keys.del(id))
148-
t.is(await keys.retrieve(id), null)
174+
t.true(await keys.del(id))
175+
t.is(await keys.retrieve(id), null)
176+
}
177+
{
178+
const { id } = await keys.create({ name: '[email protected]', plan: null })
179+
t.true(await keys.del(id))
180+
t.is(await keys.retrieve(id), null)
181+
}
149182
})
150183

151184
test('.del # error if key does not exist', async t => {
@@ -161,7 +194,10 @@ test('.del # error plan associated exist', async t => {
161194
quota: { limit: 3000, period: 'day' }
162195
})
163196

164-
const { id } = await keys.create({ name: '[email protected]', plan: plan.id })
197+
const { id } = await keys.create({
198+
199+
plan: plan.id
200+
})
165201
const error = await t.throwsAsync(keys.del(id))
166202

167203
t.true(error.message.includes('is associated with the plan'))

test/plans.js

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
'use strict'
2-
31
import { setTimeout } from 'timers/promises'
42
import openkey from 'openkey'
53
import Redis from 'ioredis'
@@ -25,26 +23,43 @@ test('.create # `name` is required', async t => {
2523
test('.create # `quota` is required', async t => {
2624
{
2725
const error = await t.throwsAsync(plans.create({ name: 'free tier' }))
28-
t.is(error.message, 'The argument `quota.period` must be `day` or `week` or `month`.')
26+
t.is(
27+
error.message,
28+
'The argument `quota.period` must be `day` or `week` or `month`.'
29+
)
2930
t.is(error.name, 'TypeError')
3031
}
3132
{
32-
const error = await t.throwsAsync(plans.create({ name: 'free tier', quota: {} }))
33-
t.is(error.message, 'The argument `quota.period` must be `day` or `week` or `month`.')
33+
const error = await t.throwsAsync(
34+
plans.create({ name: 'free tier', quota: {} })
35+
)
36+
t.is(
37+
error.message,
38+
'The argument `quota.period` must be `day` or `week` or `month`.'
39+
)
3440
t.is(error.name, 'TypeError')
3541
}
3642
{
37-
const error = await t.throwsAsync(plans.create({ name: 'free tier', quota: { period: 'today' } }))
38-
t.is(error.message, 'The argument `quota.period` must be `day` or `week` or `month`.')
43+
const error = await t.throwsAsync(
44+
plans.create({ name: 'free tier', quota: { period: 'today' } })
45+
)
46+
t.is(
47+
error.message,
48+
'The argument `quota.period` must be `day` or `week` or `month`.'
49+
)
3950
t.is(error.name, 'TypeError')
4051
}
4152
{
42-
const error = await t.throwsAsync(plans.create({ name: 'free tier', quota: { period: 'week' } }))
53+
const error = await t.throwsAsync(
54+
plans.create({ name: 'free tier', quota: { period: 'week' } })
55+
)
4356
t.is(error.message, 'The argument `quota.limit` must be a positive number.')
4457
t.is(error.name, 'TypeError')
4558
}
4659
{
47-
const error = await t.throwsAsync(plans.create({ name: 'free tier', quota: { period: 'week', limit: 0 } }))
60+
const error = await t.throwsAsync(
61+
plans.create({ name: 'free tier', quota: { period: 'week', limit: 0 } })
62+
)
4863
t.is(error.message, 'The argument `quota.limit` must be a positive number.')
4964
t.is(error.name, 'TypeError')
5065
}

0 commit comments

Comments
 (0)