• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Melchyore / adonis-cache / 4864440748

pending completion
4864440748

push

github

Oussama Benhamed
test: add missing tests for increment/decrement methods

116 of 145 branches covered (80.0%)

Branch coverage included in aggregate %.

608 of 669 relevant lines covered (90.88%)

79.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.12
/src/Repository.ts
1
import type { EmitterContract } from '@ioc:Adonis/Core/Event'
2
import type {
3
  CacheStoreContract,
4
  CacheConfig,
5
  CacheStoreConfig,
6
  AsyncFunction,
7
  RepositoryContract,
8
  PutManyResult,
9
  TaggedCacheContract,
10
  TaggableStoreContract,
11
  CacheStoresList
12
} from '@ioc:Adonis/Addons/Cache'
13

14
import { Exception } from '@poppinss/utils'
1✔
15
import { string } from '@poppinss/utils/build/helpers'
1✔
16

17
import CacheEvent from './Events/CacheEvent'
1✔
18
import CacheMissed from './Events/CacheMissed'
1✔
19
import CacheKeyWritten from './Events/CacheKeyWritten'
1✔
20
import CacheKeyForgotten from './Events/CacheKeyForgotten'
1✔
21
import CacheHit from './Events/CacheHit'
1✔
22
import Utils from './Utils'
1✔
23

24
/**
25
 * Repository class that will call the appropriate
26
 * store methods.
27
 *
28
 * @export
29
 * @class Repository
30
 * @implements {RepositoryContract<CacheStoreContract>}
31
 */
32
export default class Repository<Name extends keyof CacheStoresList>
1✔
33
  implements RepositoryContract<Name>
34
{
35
  /*
36
   * Default expiration time `ttl` = 1 hour expressed in seconds.
37
   */
38
  public static readonly DEFAULT_TTL = 60 * 60
1✔
39

40
  private _config: CacheConfig
41

42
  private _driverConfig: CacheStoreConfig
43

44
  private event: EmitterContract
45

46
  private emitter: CacheEvent
47

48
  constructor(protected _store: CacheStoreContract, private prefix: string) {}
547✔
49

50
  public get config(): CacheConfig {
51
    return this._config
×
52
  }
53

54
  public get driverConfig(): CacheStoreConfig {
55
    return this._driverConfig
4✔
56
  }
57

58
  public get getTTL(): number {
59
    return this._config.ttl || Repository.DEFAULT_TTL
260!
60
  }
61

62
  public get store(): CacheStoreContract {
63
    return this._store
103✔
64
  }
65

66
  public setConfig(config: CacheConfig, driverConfig: CacheStoreConfig): void {
67
    this._config = config
547✔
68
    this._driverConfig = driverConfig
547✔
69
    this._store.setPrefix(this.prefix)
547✔
70
  }
71

72
  public setEventDispatcher(_emitter: EmitterContract): void {
73
    this.event = _emitter
547✔
74
    this.emitter = new CacheEvent(this._config, _emitter)
547✔
75
  }
76

77
  public async get<T = any>(key: string, fallback?: T | AsyncFunction<T>): Promise<T | null> {
78
    let value = await this._store.get<T>(await this.itemKey(key))
391✔
79

80
    if (this.isNull(value)) {
391✔
81
      this.emitter.emit(new CacheMissed(key))
126✔
82

83
      if (fallback) {
126✔
84
        value = await this.resolveFallback(fallback)
36✔
85
      }
86
    } else {
87
      this.emitter.emit(new CacheHit(key, value))
265✔
88
    }
89

90
    return value
391✔
91
  }
92

93
  public async many<T extends Record<string, any>>(keys: Array<string>): Promise<T> {
94
    const records = await this._store.many<T>(keys)
52✔
95

96
    for (const [key, value] of Object.entries(records)) {
52✔
97
      if (this.isNull(value)) {
120✔
98
        this.emitter.emit(new CacheMissed(key))
26✔
99
      } else {
100
        this.emitter.emit(new CacheHit(key, value))
94✔
101
      }
102
    }
103

104
    return records
52✔
105
  }
106

107
  public async has(key: string): Promise<boolean> {
108
    return (await this._store.has(await this.itemKey(key))) === true
297✔
109
  }
110

111
  public async missing(key: string): Promise<boolean> {
112
    return !(await this.has(key))
24✔
113
  }
114

115
  public async pull<T = any>(key: string): Promise<T | null> {
116
    const value = await this.get<T>(key)
12✔
117

118
    if (!this.isNull(value)) {
12!
119
      await this.forget(key)
12✔
120
    }
121

122
    return value
12✔
123
  }
124

125
  public async put<T = any>(key: string, value: T, ttl?: number | null): Promise<boolean> {
126
    const expiration = this.calculateTTL(ttl)
272✔
127

128
    const result = await this._store.put(await this.itemKey(key), value, expiration)
248✔
129

130
    if (result) {
248!
131
      this.emitter.emit(new CacheKeyWritten(key, value, expiration))
248✔
132
    }
133

134
    return result
248✔
135
  }
136

137
  public async add<T = any>(key: string, value: T, ttl?: number | null): Promise<boolean> {
138
    if (typeof this._store['add'] === 'function') {
12✔
139
      const expiration = this.calculateTTL(ttl)
8✔
140
      const result = await this._store.add(await this.itemKey(key), value, expiration)
8✔
141

142
      if (result) {
8!
143
        this.emitter.emit(new CacheKeyWritten(key, value, expiration))
8✔
144
      }
145

146
      return result
8✔
147
    }
148

149
    const cachedValue = await this.get(key)
4✔
150

151
    if (this.isNull(cachedValue)) {
4!
152
      return await this.put(key, value, ttl)
4✔
153
    }
154

155
    return false
×
156
  }
157

158
  public async set<T = any>(key: string, value: T, ttl?: number | null): Promise<boolean> {
159
    return await this.put<T>(key, value, ttl)
12✔
160
  }
161

162
  public async increment(key: string, value: number = 1): Promise<number | boolean> {
6✔
163
    return await this._store.increment(await this.itemKey(key), value)
42✔
164
  }
165

166
  public async decrement(key: string, value: number = 1): Promise<number | boolean> {
6✔
167
    return await this._store.decrement(await this.itemKey(key), value)
42✔
168
  }
169

170
  public async putMany(list: Record<string, unknown>, ttl?: number): Promise<PutManyResult> {
171
    const expiration = this.calculateTTL(ttl)
54✔
172
    const results = await this._store.putMany(list, expiration)
54✔
173

174
    let result: PutManyResult = {}
54✔
175

176
    for (let i = 0; i < results.length; ++i) {
54✔
177
      const key = Object.keys(list)[i]
124✔
178

179
      if (results[i] === true) {
124!
180
        result[key] = true
124✔
181

182
        this.emitter.emit(new CacheKeyWritten(key, Object.values(list)[i], expiration))
124✔
183
      } else {
184
        result[key] = false
×
185
      }
186
    }
187

188
    return result
54✔
189
  }
190

191
  public async putManyForever(list: Record<string, unknown>): Promise<PutManyResult> {
192
    const results = await this._store.putManyForever(list)
13✔
193

194
    let result: PutManyResult = {}
13✔
195

196
    for (let i = 0; i < results.length; ++i) {
13✔
197
      const key = Object.keys(list)[i]
26✔
198

199
      if (results[i] === true) {
26!
200
        result[key] = true
26✔
201

202
        this.emitter.emit(new CacheKeyWritten(key, Object.values(list)[i], 0))
26✔
203
      } else {
204
        result[key] = false
×
205
      }
206
    }
207

208
    return result
13✔
209
  }
210

211
  public async forever<T = any>(key: string, value: T): Promise<boolean> {
212
    return await this._store.forever(await this.itemKey(key), value)
76✔
213
  }
214

215
  public async forget(key: string): Promise<boolean> {
216
    const result = await this._store.forget(await this.itemKey(key))
240✔
217

218
    if (result) {
240!
219
      this.emitter.emit(new CacheKeyForgotten(key))
240✔
220
    }
221

222
    return result
240✔
223
  }
224

225
  public async forgetMultiple(keys: Array<string>): Promise<Record<string, boolean>> {
226
    let result = {}
12✔
227

228
    for (const key of keys) {
12✔
229
      result[key] = await this.forget(key)
24✔
230
    }
231

232
    return result
12✔
233
  }
234

235
  public async remember<T = any>(
236
    key: string,
237
    ttl: number | null,
238
    closure: AsyncFunction<T>
239
  ): Promise<T | undefined> {
240
    if (this.checkClosure(closure)) {
48!
241
      let value = await this.get(key)
36✔
242

243
      if (!this.isNull(value)) {
36✔
244
        return value
12✔
245
      }
246

247
      value = await closure()
24✔
248

249
      await this.put(key, value, ttl)
24✔
250

251
      return value
12✔
252
    }
253
  }
254

255
  public async sear<T = any>(key: string, closure: AsyncFunction<T>): Promise<T | undefined> {
256
    return await this.rememberForever<T>(key, closure)
12✔
257
  }
258

259
  public async rememberForever<T = any>(
260
    key: string,
261
    closure: AsyncFunction<T>
262
  ): Promise<T | undefined> {
263
    if (this.checkClosure(closure)) {
72!
264
      let value = await this.get(key)
60✔
265

266
      if (!this.isNull(value)) {
60✔
267
        return value
24✔
268
      }
269

270
      value = await closure()
36✔
271

272
      await this.forever(key, value)
36✔
273

274
      return value
36✔
275
    }
276
  }
277

278
  public async flush(): Promise<boolean> {
279
    return await this._store.flush()
327✔
280
  }
281

282
  public async clear(): Promise<boolean> {
283
    return await this.flush()
234✔
284
  }
285

286
  public tags(names: string | Array<string>): TaggedCacheContract {
287
    if (!('tags' in this._store)) {
20✔
288
      throw new Exception('This cache store does not support tagging')
4✔
289
    }
290

291
    const taggedCache = (this._store as TaggableStoreContract).tags(
16✔
292
      Array.isArray(names) ? names : [names]
16!
293
    )
294
    taggedCache.setConfig(this._config, this._driverConfig)
16✔
295
    taggedCache.setEventDispatcher(this.event)
16✔
296

297
    return taggedCache
16✔
298
  }
299

300
  protected async itemKey(key: string): Promise<string> {
301
    return key
1,328✔
302
  }
303

304
  private calculateTTL(ttl: number | null | undefined): number {
305
    if (ttl && ttl < 0) {
334✔
306
      throw new Exception('Expiration time (TTL) cannot be negative')
24✔
307
    }
308

309
    const ttlInMilliseconds = Number(string.toMs(`${ttl ?? this.getTTL}s`))
310✔
310

311
    return this._store.calculateTTL!(ttlInMilliseconds)
310✔
312
  }
313

314
  private checkClosure(closure: unknown): boolean {
315
    if (!Utils.isFunction(closure)) {
120✔
316
      throw new Exception('Closure must be a function')
24✔
317
    }
318

319
    return true
96✔
320
  }
321

322
  private isNull(value: any): boolean {
323
    return value === null || value === undefined
623✔
324
  }
325

326
  private async resolveFallback<T = any>(fallback: T | AsyncFunction<T>): Promise<T> {
327
    let fallbackValue: T = Utils.isFunction(fallback) ? await fallback() : fallback
36✔
328

329
    return fallbackValue
36✔
330
  }
331
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc