@andrewshell/cacheism
Advanced tools
| # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node | ||
| # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs | ||
| name: Node.js CI | ||
| on: | ||
| push: | ||
| branches: [ master ] | ||
| pull_request: | ||
| branches: [ master ] | ||
| jobs: | ||
| build: | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| matrix: | ||
| node-version: [14.x, 16.x, 18.x] | ||
| # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ | ||
| steps: | ||
| - uses: actions/checkout@v3 | ||
| - name: Use Node.js ${{ matrix.node-version }} | ||
| uses: actions/setup-node@v3 | ||
| with: | ||
| node-version: ${{ matrix.node-version }} | ||
| cache: 'npm' | ||
| - run: npm ci | ||
| - name: Archive npm failure logs | ||
| uses: actions/upload-artifact@v3 | ||
| if: failure() | ||
| with: | ||
| name: npm-logs | ||
| path: ~/.npm/_logs | ||
| - run: npm test |
| const expect = require('expect.js'); | ||
| const mockdate = require('mockdate'); | ||
| const Cacheism = require('../lib/cacheism'); | ||
| function expectCacheHit(c, cached, data) { | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 3); | ||
| expect(c).to.have.property('cacheName', '-internal/cache'); | ||
| expect(c).to.have.property('cached', cached); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', data); | ||
| expect(c).to.have.property('etag'); | ||
| } | ||
| function expectCacheMiss(c, cached, data) { | ||
| expect(c).to.be.a(Cacheism.Miss); | ||
| expect(c).to.have.property('version', 3); | ||
| expect(c).to.have.property('cacheName', '-internal/cache'); | ||
| expect(c).to.have.property('cached', cached); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', data); | ||
| expect(c).to.have.property('etag', null); | ||
| } | ||
| function expectCacheNoErrors(c) { | ||
| expect(c).to.have.property('error', null); | ||
| expect(c).to.have.property('errorTime', null); | ||
| expect(c).to.have.property('consecutiveErrors', 0); | ||
| } | ||
| function expectCacheErrors(c, error, errors) { | ||
| expect(c).to.have.property('error', error); | ||
| expect(c).to.have.property('errorTime'); | ||
| expect(c.errorTime).to.be.a(Date); | ||
| expect(c).to.have.property('consecutiveErrors', errors); | ||
| } | ||
| function expectDataHit(d, data, etag) { | ||
| expect(d).to.be.a(Cacheism.Data); | ||
| expect(d).to.have.property('version', 3); | ||
| expect(d).to.have.property('type', Cacheism.Type.hit); | ||
| expect(d).to.have.property('created'); | ||
| expect(d.created).to.be.a(Date); | ||
| expect(d).to.have.property('data', data); | ||
| expect(d).to.have.property('etag', etag); | ||
| } | ||
| function expectDataMiss(d, data, etag) { | ||
| expect(d).to.be.a(Cacheism.Data); | ||
| expect(d).to.have.property('version', 3); | ||
| expect(d).to.have.property('type', Cacheism.Type.miss); | ||
| expect(d).to.have.property('created'); | ||
| expect(d.created).to.be.a(Date); | ||
| expect(d).to.have.property('data', data); | ||
| expect(d).to.have.property('etag', etag); | ||
| } | ||
| function expectDataNoErrors(d) { | ||
| expect(d).to.have.property('error', null); | ||
| expect(d).to.have.property('errorTime', null); | ||
| expect(d).to.have.property('consecutiveErrors', 0); | ||
| } | ||
| function expectDataErrors(d, error, errors) { | ||
| expect(d).to.have.property('error', error); | ||
| expect(d).to.have.property('errorTime'); | ||
| expect(d.errorTime).to.be.a(Date); | ||
| expect(d).to.have.property('consecutiveErrors', errors); | ||
| } | ||
| module.exports = { | ||
| expectCacheHit, | ||
| expectCacheMiss, | ||
| expectCacheNoErrors, | ||
| expectCacheErrors, | ||
| expectDataHit, | ||
| expectDataMiss, | ||
| expectDataNoErrors, | ||
| expectDataErrors, | ||
| }; |
+21
-12
@@ -10,2 +10,3 @@ const common = require('./common'); | ||
| this.status = common.Status; | ||
| this.type = common.Type; | ||
| } | ||
@@ -16,12 +17,12 @@ | ||
| try { | ||
| let existing = new common.Miss(name, new Error('Missing cache'), 0); | ||
| let hasCache = await this.store.isset(name); | ||
| let existing = new common.Miss(name, new Error('Missing cache')); | ||
| let hasCache = await this.store.isset(name); | ||
| if (hasCache) { | ||
| existing = (await this.store.get(name)).response(); | ||
| } | ||
| if (hasCache) { | ||
| existing = await this.store.get(name); | ||
| } | ||
| try { | ||
| if (status >= this.status.preferCache && hasCache) { | ||
| if (status >= this.status.preferCache && hasCache && existing.isHit) { | ||
| response = existing; | ||
@@ -35,3 +36,2 @@ } else if (status === this.status.onlyCache) { | ||
| } | ||
| await this.store.set(response); | ||
| } | ||
@@ -41,7 +41,13 @@ | ||
| if (status >= this.status.cacheOnFail && await this.store.isset(name)) { | ||
| response = await this.store.get(name); | ||
| response.error = err; | ||
| if (status >= this.status.cacheOnFail && hasCache) { | ||
| response = existing; | ||
| response.error = err.toString(); | ||
| response.errorTime = new Date(); | ||
| response.consecutiveErrors++; | ||
| } else { | ||
| response = new common.Miss(name, err); | ||
| response = new common.Miss( | ||
| name, | ||
| err.toString(), | ||
| existing.consecutiveErrors + 1 | ||
| ); | ||
| } | ||
@@ -51,2 +57,4 @@ | ||
| await this.store.set(common.Data.fromResponse(response)); | ||
| Object.freeze(response); | ||
@@ -76,2 +84,3 @@ return response; | ||
| Cacheism.Status = common.Status; | ||
| Cacheism.Type = common.Type; | ||
@@ -78,0 +87,0 @@ Cacheism.store = { |
+100
-14
@@ -10,2 +10,7 @@ const generateEtag = require('etag'); | ||
| class Type {} | ||
| Type.hit = 'Hit'; | ||
| Type.miss = 'Miss'; | ||
| Object.freeze(Status); | ||
@@ -15,3 +20,3 @@ | ||
| constructor(name, data, etag) { | ||
| this.version = 2; | ||
| this.version = 3; | ||
| this.cacheName = name; | ||
@@ -22,2 +27,4 @@ this.cached = false; | ||
| this.error = null; | ||
| this.errorTime = null; | ||
| this.consecutiveErrors = 0; | ||
| this.etag = null == etag ? generateEtag(JSON.stringify(data)) : etag; | ||
@@ -30,4 +37,4 @@ this.isHit = true; | ||
| class Miss { | ||
| constructor(name, error) { | ||
| this.version = 2; | ||
| constructor(name, error, consecutiveErrors) { | ||
| this.version = 3; | ||
| this.cacheName = name; | ||
@@ -38,2 +45,4 @@ this.cached = false; | ||
| this.error = error; | ||
| this.errorTime = new Date(this.created); | ||
| this.consecutiveErrors = consecutiveErrors; | ||
| this.etag = null; | ||
@@ -46,20 +55,34 @@ this.isHit = false; | ||
| class Data { | ||
| constructor(version, name, created, data, etag) { | ||
| constructor(version, type, name, created, data, error, errorTime, consecutiveErrors, etag) { | ||
| this.version = version; | ||
| this.type = type; | ||
| this.cacheName = name; | ||
| this.created = created; | ||
| this.data = data; | ||
| this.error = error; | ||
| this.errorTime = errorTime; | ||
| this.consecutiveErrors = consecutiveErrors; | ||
| this.etag = etag; | ||
| } | ||
| hit() { | ||
| if (2 !== this.version) { | ||
| response() { | ||
| if (3 !== this.version) { | ||
| throw new Error(`Unknown cache version number: ${this.version}`); | ||
| } | ||
| const hit = new Hit(this.cacheName, this.data, this.etag); | ||
| hit.cached = true; | ||
| hit.created = this.created; | ||
| let response; | ||
| return hit; | ||
| if (this.type === Type.hit) { | ||
| response = new Hit(this.cacheName, this.data, this.etag); | ||
| response.cached = true; | ||
| response.created = this.created; | ||
| response.consecutiveErrors = this.consecutiveErrors; | ||
| } else { | ||
| response = new Miss(this.cacheName, this.error, this.consecutiveErrors); | ||
| response.cached = false; | ||
| response.created = this.created; | ||
| response.errorTime = this.errorTime; | ||
| } | ||
| return response; | ||
| } | ||
@@ -70,5 +93,9 @@ | ||
| version: this.version, | ||
| type: this.type, | ||
| cacheName: this.cacheName, | ||
| created: this.created, | ||
| data: this.data, | ||
| error: this.error, | ||
| errorTime: this.errorTime, | ||
| consecutiveErrors: this.consecutiveErrors, | ||
| etag: this.etag, | ||
@@ -78,4 +105,14 @@ }, null, 2); | ||
| static fromHit(hit) { | ||
| return new Data(hit.version, hit.cacheName, hit.created, hit.data, hit.etag); | ||
| static fromResponse(response) { | ||
| return new Data( | ||
| response.version, | ||
| response.isHit ? Type.hit : Type.miss, | ||
| response.cacheName, | ||
| response.created, | ||
| response.data, | ||
| response.error, | ||
| response.errorTime, | ||
| response.consecutiveErrors, | ||
| response.etag | ||
| ); | ||
| } | ||
@@ -85,6 +122,55 @@ | ||
| const parsed = JSON.parse(value); | ||
| return new Data(parsed.version, parsed.cacheName, new Date(parsed.created), parsed.data, parsed.etag); | ||
| if (2 === parsed.version) { | ||
| return new Data( | ||
| 3, | ||
| Type.hit, | ||
| parsed.cacheName, | ||
| new Date(parsed.created), | ||
| parsed.data, | ||
| null, | ||
| null, | ||
| 0, | ||
| parsed.etag | ||
| ); | ||
| } | ||
| if (3 === parsed.version) { | ||
| return new Data( | ||
| 3, | ||
| parsed.type, | ||
| parsed.cacheName, | ||
| new Date(parsed.created), | ||
| parsed.data, | ||
| parsed.error, | ||
| null === parsed.errorTime ? null : new Date(parsed.errorTime), | ||
| parsed.consecutiveErrors, | ||
| parsed.etag | ||
| ); | ||
| } | ||
| throw new Error(`Unknown cache version number: ${parsed.version}`); | ||
| } | ||
| } | ||
| module.exports = { Hit, Miss, Data, Status }; | ||
| module.exports = { Hit, Miss, Data, Status, Type }; | ||
| /** | ||
| * String.prototype.replaceAll() polyfill | ||
| * https://gomakethings.com/how-to-replace-a-section-of-a-string-with-another-one-with-vanilla-js/ | ||
| * @author Chris Ferdinandi | ||
| * @license MIT | ||
| */ | ||
| if (!String.prototype.replaceAll) { | ||
| String.prototype.replaceAll = function(str, newStr){ | ||
| // If a regex pattern | ||
| if (Object.prototype.toString.call(str).toLowerCase() === '[object regexp]') { | ||
| return this.replace(str, newStr); | ||
| } | ||
| // If a string | ||
| return this.replace(new RegExp(str, 'g'), newStr); | ||
| }; | ||
| } |
@@ -19,9 +19,9 @@ const common = require('./common'); | ||
| const data = await fsPromises.readFile(filename, 'utf8'); | ||
| return common.Data.parse(data).hit(); | ||
| return common.Data.parse(data); | ||
| } | ||
| storeFilesystem.set = async (hit) => { | ||
| const filename = path.resolve(config.datadir, `${hit.cacheName}.json`); | ||
| storeFilesystem.set = async (data) => { | ||
| const filename = path.resolve(config.datadir, `${data.cacheName}.json`); | ||
| _mkdir(path.dirname(filename)); | ||
| await fsPromises.writeFile(filename, common.Data.fromHit(hit).stringify(), 'utf8'); | ||
| await fsPromises.writeFile(filename, data.stringify(), 'utf8'); | ||
| } | ||
@@ -28,0 +28,0 @@ |
@@ -7,7 +7,7 @@ const common = require('./common'); | ||
| storeMemory.get = async (cacheName) => { | ||
| return common.Data.parse(storeMemory.data[cacheName]).hit(); | ||
| return common.Data.parse(storeMemory.data[cacheName]); | ||
| } | ||
| storeMemory.set = async (hit) => { | ||
| storeMemory.data[hit.cacheName] = common.Data.fromHit(hit).stringify(); | ||
| storeMemory.set = async (data) => { | ||
| storeMemory.data[data.cacheName] = data.stringify(); | ||
| } | ||
@@ -14,0 +14,0 @@ |
+3
-2
| { | ||
| "name": "@andrewshell/cacheism", | ||
| "version": "1.0.0", | ||
| "version": "2.0.0", | ||
| "description": "Simple caching library", | ||
@@ -25,3 +25,4 @@ "main": "lib/cacheism.js", | ||
| "expect.js": "^0.3.1", | ||
| "mocha": "^9.2.2" | ||
| "mocha": "^10.2.0", | ||
| "mockdate": "^3.0.5" | ||
| }, | ||
@@ -28,0 +29,0 @@ "dependencies": { |
+15
-9
| # cacheism | ||
| Simple caching library | ||
| [](https://github.com/andrewshell/cacheism/actions/workflows/node.js.yml) | ||
| ## Overview | ||
@@ -72,11 +74,13 @@ | ||
| Hit { | ||
| version: 2, | ||
| version: 3, | ||
| cacheName: '-internal/hoopla', | ||
| cached: true, | ||
| created: 2022-04-22T21:05:14.094Z, | ||
| created: 2023-04-02T22:00:49.320Z, | ||
| data: { message: 'Hoopla!' }, | ||
| error: Error: Death | ||
| at /Users/andrewshell/code/personal/test-cacheism/index.js:8:15 | ||
| at Cacheism.go (/Users/andrewshell/code/personal/cacheism/lib/cacheism.js:29:30) | ||
| at async run (/Users/andrewshell/code/personal/test-cacheism/index.js:7:13), | ||
| at /Users/andrewshell/code/test-cacheism/index.js:9:19 | ||
| at Cacheism.go (/Users/andrewshell/code/cacheism/lib/cacheism.js:30:30) | ||
| at async run (/Users/andrewshell/code/test-cacheism/index.js:7:18), | ||
| errorTime: 2023-04-02T22:00:49.928Z, | ||
| consecutiveErrors: 1, | ||
| etag: '"15-QcHvuZdyxCmLJ4zoYIPsP6pkNoM"', | ||
@@ -99,10 +103,12 @@ isHit: true, | ||
| Miss { | ||
| version: 2, | ||
| version: 3, | ||
| cacheName: '-internal/hoopla', | ||
| cached: false, | ||
| created: 2022-04-22T21:30:56.275Z, | ||
| created: 2023-04-02T22:02:30.294Z, | ||
| data: null, | ||
| error: Error: Missing cache | ||
| at Cacheism.go (/Users/andrewshell/code/personal/cacheism/lib/cacheism.js:27:19) | ||
| at async run (/Users/andrewshell/code/personal/test-cacheism/index.js:7:18), | ||
| at Cacheism.go (/Users/andrewshell/code/cacheism/lib/cacheism.js:28:19) | ||
| at async run (/Users/andrewshell/code/test-cacheism/index.js:7:18), | ||
| errorTime: 2023-04-02T22:02:30.294Z, | ||
| consecutiveErrors: 1, | ||
| etag: null, | ||
@@ -109,0 +115,0 @@ isHit: false, |
+302
-154
| const expect = require('expect.js'); | ||
| const mockdate = require('mockdate'); | ||
| const fs = require('fs'); | ||
@@ -9,2 +11,4 @@ const path = require('path'); | ||
| const helpers = require('./helpers'); | ||
| describe('filesystem', function() { | ||
@@ -32,47 +36,88 @@ | ||
| it('should return a Hit with the live value', async function () { | ||
| const c = await cache.go('-internal', 'nocache', Cacheism.Status.onlyFresh, async () => { | ||
| return 'live'; | ||
| describe('and no existing cache', async function () { | ||
| it('should return a Hit (live value) on success', async function () { | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.onlyFresh, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheHit(c, false, 'live'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'live', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/nocache'); | ||
| expect(c).to.have.property('cached', false); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', 'live'); | ||
| expect(c).to.have.property('error', null); | ||
| expect(c).to.have.property('etag'); | ||
| }); | ||
| it('should return a Miss on error', async function () { | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.onlyFresh, async () => { | ||
| throw Error('cache error'); | ||
| }); | ||
| it('should return a Miss on error', async function () { | ||
| const c = await cache.go('-internal', 'nocache', Cacheism.Status.onlyFresh, async () => { | ||
| throw Error('cache error'); | ||
| helpers.expectCacheMiss(c, false, null); | ||
| helpers.expectCacheErrors(c, 'cache error', 1); | ||
| expect(c.created).to.eql(c.errorTime); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataMiss(d, null); | ||
| helpers.expectDataErrors(d, 'Error: cache error', 1); | ||
| expect(d.created).to.eql(d.errorTime); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Miss); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/nocache'); | ||
| expect(c).to.have.property('error'); | ||
| expect(c.error).to.be.an(Error); | ||
| expect(c.error).to.have.property('message', 'cache error'); | ||
| }); | ||
| it('should update cache with successful live value', async function () { | ||
| const c = await cache.go('-internal', 'true', Cacheism.Status.onlyFresh, async () => { | ||
| return 'live'; | ||
| describe('and having existing cache', async function () { | ||
| it('should return a Hit (live value) on success', async function () { | ||
| mockdate.set('2000-11-22'); | ||
| await cache.store.set(Cacheism.Data.fromResponse( | ||
| new Cacheism.Hit('-internal/cache', 'cached') | ||
| )); | ||
| mockdate.reset(); | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.onlyFresh, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheHit(c, false, 'live'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'live', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/true'); | ||
| expect(c).to.have.property('cached', false); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', 'live'); | ||
| expect(c).to.have.property('error', null); | ||
| expect(c).to.have.property('etag'); | ||
| it('should return a Miss on error', async function () { | ||
| mockdate.set('2000-11-22'); | ||
| expect(await cache.store.isset('-internal/true')).to.be(true); | ||
| await cache.store.set(Cacheism.Data.fromResponse( | ||
| new Cacheism.Hit('-internal/cache', 'cached') | ||
| )); | ||
| mockdate.reset(); | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.onlyFresh, async () => { | ||
| throw Error('cache error'); | ||
| }); | ||
| helpers.expectCacheMiss(c, false, null); | ||
| helpers.expectCacheErrors(c, 'cache error', 1); | ||
| expect(c.created).to.eql(c.errorTime); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataMiss(d, null); | ||
| helpers.expectDataErrors(d, 'Error: cache error', 1); | ||
| expect(d.created).to.eql(d.errorTime); | ||
| }); | ||
| }); | ||
@@ -84,69 +129,88 @@ | ||
| it('should return a Miss on error with no cache', async function () { | ||
| const c = await cache.go('-internal', 'nocache', Cacheism.Status.cacheOnFail, async () => { | ||
| throw Error('cache error'); | ||
| describe('and no existing cache', async function () { | ||
| it('should return a Hit (live value) on success', async function () { | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.cacheOnFail, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheHit(c, false, 'live'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'live', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Miss); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/nocache'); | ||
| expect(c).to.have.property('error'); | ||
| expect(c.error).to.be.an(Error); | ||
| expect(c.error).to.have.property('message', 'cache error'); | ||
| }); | ||
| it('should return a Miss on error', async function () { | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.cacheOnFail, async () => { | ||
| throw Error('cache error'); | ||
| }); | ||
| it('should return a Hit on error with cache', async function () { | ||
| await cache.store.set(new Cacheism.Hit('-internal/true', 'cached')); | ||
| helpers.expectCacheMiss(c, false, null); | ||
| helpers.expectCacheErrors(c, 'cache error', 1); | ||
| expect(c.created).to.eql(c.errorTime); | ||
| const c = await cache.go('-internal', 'true', Cacheism.Status.cacheOnFail, async () => { | ||
| throw Error('cache error'); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataMiss(d, null, c.etag); | ||
| helpers.expectDataErrors(d, 'Error: cache error', 1); | ||
| expect(d.created).to.eql(d.errorTime); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/true'); | ||
| expect(c).to.have.property('cached', true); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', 'cached'); | ||
| expect(c).to.have.property('error'); | ||
| expect(c.error).to.be.an(Error); | ||
| expect(c.error).to.have.property('message', 'cache error'); | ||
| expect(c).to.have.property('etag'); | ||
| }); | ||
| it('should return a Hit with the live value with cache', async function () { | ||
| await cache.store.set(new Cacheism.Hit('-internal/true', 'cached')); | ||
| describe('and having existing cache', async function () { | ||
| const c = await cache.go('-internal', 'true', Cacheism.Status.cacheOnFail, async () => { | ||
| return 'live'; | ||
| it('should return a Hit (live value) on success', async function () { | ||
| mockdate.set('2000-11-22'); | ||
| await cache.store.set(Cacheism.Data.fromResponse( | ||
| new Cacheism.Hit('-internal/cache', 'cached') | ||
| )); | ||
| mockdate.reset(); | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.cacheOnFail, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheHit(c, false, 'live'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'live', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/true'); | ||
| expect(c).to.have.property('cached', false); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', 'live'); | ||
| expect(c).to.have.property('error', null); | ||
| expect(c).to.have.property('etag'); | ||
| }); | ||
| it('should return a Hit (cached value) on error', async function () { | ||
| mockdate.set('2000-11-22'); | ||
| it('should update cache with successful live value', async function () { | ||
| await cache.store.set(new Cacheism.Hit('-internal/true', 'cached')); | ||
| await cache.store.set(Cacheism.Data.fromResponse( | ||
| new Cacheism.Hit('-internal/cache', 'cached') | ||
| )); | ||
| const c = await cache.go('-internal', 'true', Cacheism.Status.cacheOnFail, async () => { | ||
| return 'live'; | ||
| mockdate.reset(); | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.cacheOnFail, async () => { | ||
| throw Error('cache error'); | ||
| }); | ||
| helpers.expectCacheHit(c, true, 'cached'); | ||
| helpers.expectCacheErrors(c, 'cache error', 1); | ||
| expect(c.created).not.to.eql(c.errorTime); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'cached', c.etag); | ||
| helpers.expectDataErrors(d, 'Error: cache error', 1); | ||
| expect(d.created).not.to.eql(d.errorTime); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/true'); | ||
| expect(c).to.have.property('cached', false); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', 'live'); | ||
| expect(c).to.have.property('error', null); | ||
| expect(c).to.have.property('etag'); | ||
| }); | ||
@@ -158,47 +222,86 @@ | ||
| it('should return a Miss on error with no cache', async function () { | ||
| const c = await cache.go('-internal', 'nocache', Cacheism.Status.preferCache, async () => { | ||
| throw Error('cache error'); | ||
| describe('and no existing cache', async function () { | ||
| it('should return a Hit (live value) on success', async function () { | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.preferCache, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheHit(c, false, 'live'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'live', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Miss); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/nocache'); | ||
| expect(c).to.have.property('error'); | ||
| expect(c.error).to.be.an(Error); | ||
| expect(c.error).to.have.property('message', 'cache error'); | ||
| }); | ||
| it('should return a Miss on error', async function () { | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.preferCache, async () => { | ||
| throw Error('cache error'); | ||
| }); | ||
| it('should return a Hit with the cached value with cache', async function () { | ||
| await cache.store.set(new Cacheism.Hit('-internal/true', 'cached')); | ||
| helpers.expectCacheMiss(c, false, null); | ||
| helpers.expectCacheErrors(c, 'cache error', 1); | ||
| expect(c.created).to.eql(c.errorTime); | ||
| const c = await cache.go('-internal', 'true', Cacheism.Status.preferCache, async () => { | ||
| return 'live'; | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataMiss(d, null, c.etag); | ||
| helpers.expectDataErrors(d, 'Error: cache error', 1); | ||
| expect(d.created).to.eql(d.errorTime); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/true'); | ||
| expect(c).to.have.property('cached', true); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', 'cached'); | ||
| expect(c).to.have.property('error', null); | ||
| expect(c).to.have.property('etag'); | ||
| }); | ||
| it('should return a Hit with the live value with no cache', async function () { | ||
| const c = await cache.go('-internal', 'true', Cacheism.Status.preferCache, async () => { | ||
| return 'live'; | ||
| describe('and having existing cache', async function () { | ||
| it('should return a Hit (cached value) on success', async function () { | ||
| mockdate.set('2000-11-22'); | ||
| await cache.store.set(Cacheism.Data.fromResponse( | ||
| new Cacheism.Hit('-internal/cache', 'cached') | ||
| )); | ||
| mockdate.reset(); | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.preferCache, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheHit(c, true, 'cached'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'cached', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/true'); | ||
| expect(c).to.have.property('cached', false); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', 'live'); | ||
| expect(c).to.have.property('error', null); | ||
| expect(c).to.have.property('etag'); | ||
| it('should return a Hit (cached value) on error', async function () { | ||
| mockdate.set('2000-11-22'); | ||
| await cache.store.set(Cacheism.Data.fromResponse( | ||
| new Cacheism.Hit('-internal/cache', 'cached') | ||
| )); | ||
| mockdate.reset(); | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.preferCache, async () => { | ||
| throw Error('cache error'); | ||
| }); | ||
| helpers.expectCacheHit(c, true, 'cached'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'cached', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| }); | ||
@@ -209,44 +312,89 @@ | ||
| describe('when status=onlyCache', function () { | ||
| it('should return a Miss on error with no cache', async function () { | ||
| const c = await cache.go('-internal', 'nocache', Cacheism.Status.onlyCache, async () => { | ||
| throw Error('cache error'); | ||
| describe('and no existing cache', async function () { | ||
| it('should return a Miss on success', async function () { | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.onlyCache, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheMiss(c, false, null); | ||
| helpers.expectCacheErrors(c, 'Missing cache', 1); | ||
| expect(c.created).to.eql(c.errorTime); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataMiss(d, null, null); | ||
| helpers.expectDataErrors(d, 'Error: Missing cache', 1); | ||
| expect(d.created).to.eql(d.errorTime); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Miss); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/nocache'); | ||
| expect(c).to.have.property('error'); | ||
| expect(c.error).to.be.an(Error); | ||
| expect(c.error).to.have.property('message', 'Missing cache'); | ||
| }); | ||
| it('should return a Miss on error', async function () { | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.onlyCache, async () => { | ||
| throw Error('cache error'); | ||
| }); | ||
| it('should return a Hit with the cached value with cache', async function () { | ||
| await cache.store.set(new Cacheism.Hit('-internal/true', 'cached')); | ||
| helpers.expectCacheMiss(c, false, null); | ||
| helpers.expectCacheErrors(c, 'Missing cache', 1); | ||
| expect(c.created).to.eql(c.errorTime); | ||
| const c = await cache.go('-internal', 'true', Cacheism.Status.onlyCache, async () => { | ||
| return 'live'; | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataMiss(d, null, null); | ||
| helpers.expectDataErrors(d, 'Error: Missing cache', 1); | ||
| expect(d.created).to.eql(d.errorTime); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/true'); | ||
| expect(c).to.have.property('cached', true); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', 'cached'); | ||
| expect(c).to.have.property('error', null); | ||
| expect(c).to.have.property('etag'); | ||
| }); | ||
| it('should return a Miss with no cache', async function () { | ||
| const c = await cache.go('-internal', 'nocache', Cacheism.Status.onlyCache, async () => { | ||
| return 'live'; | ||
| describe('and having existing cache', async function () { | ||
| it('should return a Hit (cached value) on success', async function () { | ||
| mockdate.set('2000-11-22'); | ||
| await cache.store.set(Cacheism.Data.fromResponse( | ||
| new Cacheism.Hit('-internal/cache', 'cached') | ||
| )); | ||
| mockdate.reset(); | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.onlyCache, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheHit(c, true, 'cached'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'cached', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Miss); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/nocache'); | ||
| expect(c).to.have.property('error'); | ||
| expect(c.error).to.be.an(Error); | ||
| expect(c.error).to.have.property('message', 'Missing cache'); | ||
| it('should return a Hit (cached value) on error', async function () { | ||
| mockdate.set('2000-11-22'); | ||
| await cache.store.set(Cacheism.Data.fromResponse( | ||
| new Cacheism.Hit('-internal/cache', 'cached') | ||
| )); | ||
| mockdate.reset(); | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.onlyCache, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheHit(c, true, 'cached'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'cached', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| }); | ||
@@ -253,0 +401,0 @@ |
+307
-175
| const expect = require('expect.js'); | ||
| const mockdate = require('mockdate'); | ||
@@ -6,4 +7,6 @@ const Cacheism = require('../lib/cacheism'); | ||
| describe('memory', function() { | ||
| const helpers = require('./helpers'); | ||
| describe.only('memory', function() { | ||
| beforeEach(function() { | ||
@@ -20,52 +23,90 @@ // runs before each test in this block | ||
| it('should return a Hit with the live value', async function () { | ||
| const c = await cache.go('-internal', 'nocache', Cacheism.Status.onlyFresh, async () => { | ||
| return 'live'; | ||
| describe('and no existing cache', async function () { | ||
| it('should return a Hit (live value) on success', async function () { | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.onlyFresh, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheHit(c, false, 'live'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'live', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/nocache'); | ||
| expect(c).to.have.property('cached', false); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', 'live'); | ||
| expect(c).to.have.property('error', null); | ||
| expect(c).to.have.property('etag'); | ||
| }); | ||
| it('should return a Miss on error', async function () { | ||
| let c, d, e; | ||
| for (e = 1; e < 3; e++) { | ||
| c = await cache.go('-internal', 'cache', Cacheism.Status.onlyFresh, async () => { | ||
| throw Error('cache error'); | ||
| }); | ||
| it('should return a Miss on error', async function () { | ||
| const c = await cache.go('-internal', 'nocache', Cacheism.Status.onlyFresh, async () => { | ||
| throw Error('cache error'); | ||
| helpers.expectCacheMiss(c, false, null); | ||
| helpers.expectCacheErrors(c, 'Error: cache error', e); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataMiss(d, null); | ||
| helpers.expectDataErrors(d, 'Error: cache error', e); | ||
| } | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Miss); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/nocache'); | ||
| expect(c).to.have.property('error'); | ||
| expect(c.error).to.be.an(Error); | ||
| expect(c.error).to.have.property('message', 'cache error'); | ||
| }); | ||
| it('should update cache with successful live value', async function () { | ||
| const c = await cache.go('-internal', 'true', Cacheism.Status.onlyFresh, async () => { | ||
| return 'live'; | ||
| describe('and having existing cache', async function () { | ||
| it('should return a Hit (live value) on success', async function () { | ||
| mockdate.set('2000-11-22'); | ||
| await cache.store.set(Cacheism.Data.fromResponse( | ||
| new Cacheism.Hit('-internal/cache', 'cached') | ||
| )); | ||
| mockdate.reset(); | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.onlyFresh, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheHit(c, false, 'live'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'live', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/true'); | ||
| expect(c).to.have.property('cached', false); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', 'live'); | ||
| expect(c).to.have.property('error', null); | ||
| expect(c).to.have.property('etag'); | ||
| it('should return a Miss on error', async function () { | ||
| mockdate.set('2000-11-22'); | ||
| expect(cache.store.data).to.have.property('-internal/true'); | ||
| await cache.store.set(Cacheism.Data.fromResponse( | ||
| new Cacheism.Hit('-internal/cache', 'cached') | ||
| )); | ||
| const d = Cacheism.Data.parse(cache.store.data['-internal/true']); | ||
| expect(d).to.be.a(Cacheism.Data); | ||
| expect(d).to.have.property('data', 'live'); | ||
| expect(d).to.have.property('etag', c.etag); | ||
| mockdate.reset(); | ||
| let c, d, e; | ||
| for (e = 1; e < 3; e++) { | ||
| c = await cache.go('-internal', 'cache', Cacheism.Status.onlyFresh, async () => { | ||
| throw Error('cache error'); | ||
| }); | ||
| helpers.expectCacheMiss(c, false, null); | ||
| helpers.expectCacheErrors(c, 'Error: cache error', e); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataMiss(d, null); | ||
| helpers.expectDataErrors(d, 'Error: cache error', e); | ||
| } | ||
| }); | ||
| }); | ||
@@ -77,82 +118,90 @@ | ||
| it('should return a Miss on error with no cache', async function () { | ||
| const c = await cache.go('-internal', 'nocache', Cacheism.Status.cacheOnFail, async () => { | ||
| throw Error('cache error'); | ||
| }); | ||
| describe('and no existing cache', async function () { | ||
| expect(c).to.be.a(Cacheism.Miss); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/nocache'); | ||
| expect(c).to.have.property('error'); | ||
| expect(c.error).to.be.an(Error); | ||
| expect(c.error).to.have.property('message', 'cache error'); | ||
| }); | ||
| it('should return a Hit (live value) on success', async function () { | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.cacheOnFail, async () => { | ||
| return 'live'; | ||
| }); | ||
| it('should return a Hit on error with cache', async function () { | ||
| cache.store.data['-internal/true'] = Cacheism.Data.fromHit( | ||
| new Cacheism.Hit('-internal/true', 'cached') | ||
| ).stringify(); | ||
| helpers.expectCacheHit(c, false, 'live'); | ||
| helpers.expectCacheNoErrors(c); | ||
| const c = await cache.go('-internal', 'true', Cacheism.Status.cacheOnFail, async () => { | ||
| throw Error('cache error'); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'live', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/true'); | ||
| expect(c).to.have.property('cached', true); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', 'cached'); | ||
| expect(c).to.have.property('error'); | ||
| expect(c.error).to.be.an(Error); | ||
| expect(c.error).to.have.property('message', 'cache error'); | ||
| expect(c).to.have.property('etag'); | ||
| }); | ||
| it('should return a Miss on error', async function () { | ||
| let c, d, e; | ||
| for (e = 1; e < 3; e++) { | ||
| c = await cache.go('-internal', 'cache', Cacheism.Status.cacheOnFail, async () => { | ||
| throw Error('cache error'); | ||
| }); | ||
| it('should return a Hit with the live value with cache', async function () { | ||
| cache.store.data['-internal/true'] = Cacheism.Data.fromHit( | ||
| new Cacheism.Hit('-internal/true', 'cached') | ||
| ).stringify(); | ||
| helpers.expectCacheMiss(c, false, null); | ||
| helpers.expectCacheErrors(c, 'Error: cache error', e); | ||
| const c = await cache.go('-internal', 'true', Cacheism.Status.cacheOnFail, async () => { | ||
| return 'live'; | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataMiss(d, null, c.etag); | ||
| helpers.expectDataErrors(d, 'Error: cache error', e); | ||
| } | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/true'); | ||
| expect(c).to.have.property('cached', false); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', 'live'); | ||
| expect(c).to.have.property('error', null); | ||
| expect(c).to.have.property('etag'); | ||
| }); | ||
| it('should update cache with successful live value', async function () { | ||
| cache.store.data['-internal/true'] = Cacheism.Data.fromHit( | ||
| new Cacheism.Hit('-internal/true', 'cached') | ||
| ).stringify(); | ||
| describe('and having existing cache', async function () { | ||
| const c = await cache.go('-internal', 'true', Cacheism.Status.cacheOnFail, async () => { | ||
| return 'live'; | ||
| it('should return a Hit (live value) on success', async function () { | ||
| mockdate.set('2000-11-22'); | ||
| await cache.store.set(Cacheism.Data.fromResponse( | ||
| new Cacheism.Hit('-internal/cache', 'cached') | ||
| )); | ||
| mockdate.reset(); | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.cacheOnFail, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheHit(c, false, 'live'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'live', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/true'); | ||
| expect(c).to.have.property('cached', false); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', 'live'); | ||
| expect(c).to.have.property('error', null); | ||
| expect(c).to.have.property('etag'); | ||
| it('should return a Hit (cached value) on error', async function () { | ||
| mockdate.set('2000-11-22'); | ||
| expect(cache.store.data).to.have.property('-internal/true'); | ||
| await cache.store.set(Cacheism.Data.fromResponse( | ||
| new Cacheism.Hit('-internal/cache', 'cached') | ||
| )); | ||
| const d = Cacheism.Data.parse(cache.store.data['-internal/true']); | ||
| expect(d).to.be.a(Cacheism.Data); | ||
| expect(d).to.have.property('data', 'live'); | ||
| expect(d).to.have.property('etag', c.etag); | ||
| mockdate.reset(); | ||
| let c, d, e; | ||
| for (e = 1; e < 3; e++) { | ||
| c = await cache.go('-internal', 'cache', Cacheism.Status.cacheOnFail, async () => { | ||
| throw Error('cache error'); | ||
| }); | ||
| helpers.expectCacheHit(c, true, 'cached'); | ||
| helpers.expectCacheErrors(c, 'Error: cache error', e); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'cached', c.etag); | ||
| helpers.expectDataErrors(d, 'Error: cache error', e); | ||
| } | ||
| }); | ||
| }); | ||
@@ -164,49 +213,90 @@ | ||
| it('should return a Miss on error with no cache', async function () { | ||
| const c = await cache.go('-internal', 'nocache', Cacheism.Status.preferCache, async () => { | ||
| throw Error('cache error'); | ||
| describe('and no existing cache', async function () { | ||
| it('should return a Hit (live value) on success', async function () { | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.preferCache, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheHit(c, false, 'live'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'live', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Miss); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/nocache'); | ||
| expect(c).to.have.property('error'); | ||
| expect(c.error).to.be.an(Error); | ||
| expect(c.error).to.have.property('message', 'cache error'); | ||
| }); | ||
| it('should return a Miss on error', async function () { | ||
| let c, d, e; | ||
| for (e = 1; e < 3; e++) { | ||
| c = await cache.go('-internal', 'cache', Cacheism.Status.preferCache, async () => { | ||
| throw Error('cache error'); | ||
| }); | ||
| it('should return a Hit with the cached value with cache', async function () { | ||
| cache.store.data['-internal/true'] = Cacheism.Data.fromHit( | ||
| new Cacheism.Hit('-internal/true', 'cached') | ||
| ).stringify(); | ||
| helpers.expectCacheMiss(c, false, null); | ||
| helpers.expectCacheErrors(c, 'Error: cache error', e); | ||
| const c = await cache.go('-internal', 'true', Cacheism.Status.preferCache, async () => { | ||
| return 'live'; | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataMiss(d, null, c.etag); | ||
| helpers.expectDataErrors(d, 'Error: cache error', e); | ||
| } | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/true'); | ||
| expect(c).to.have.property('cached', true); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', 'cached'); | ||
| expect(c).to.have.property('error', null); | ||
| expect(c).to.have.property('etag'); | ||
| }); | ||
| it('should return a Hit with the live value with no cache', async function () { | ||
| const c = await cache.go('-internal', 'true', Cacheism.Status.preferCache, async () => { | ||
| return 'live'; | ||
| describe('and having existing cache', async function () { | ||
| it('should return a Hit (cached value) on success', async function () { | ||
| mockdate.set('2000-11-22'); | ||
| await cache.store.set(Cacheism.Data.fromResponse( | ||
| new Cacheism.Hit('-internal/cache', 'cached') | ||
| )); | ||
| mockdate.reset(); | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.preferCache, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheHit(c, true, 'cached'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'cached', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/true'); | ||
| expect(c).to.have.property('cached', false); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', 'live'); | ||
| expect(c).to.have.property('error', null); | ||
| expect(c).to.have.property('etag'); | ||
| it('should return a Hit (cached value) on error', async function () { | ||
| mockdate.set('2000-11-22'); | ||
| await cache.store.set(Cacheism.Data.fromResponse( | ||
| new Cacheism.Hit('-internal/cache', 'cached') | ||
| )); | ||
| mockdate.reset(); | ||
| let c, d, e; | ||
| for (e = 1; e < 3; e++) { | ||
| c = await cache.go('-internal', 'cache', Cacheism.Status.preferCache, async () => { | ||
| throw Error('cache error'); | ||
| }); | ||
| helpers.expectCacheHit(c, true, 'cached'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'cached', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| } | ||
| }); | ||
| }); | ||
@@ -217,46 +307,88 @@ | ||
| describe('when status=onlyCache', function () { | ||
| it('should return a Miss on error with no cache', async function () { | ||
| const c = await cache.go('-internal', 'nocache', Cacheism.Status.onlyCache, async () => { | ||
| throw Error('cache error'); | ||
| describe('and no existing cache', async function () { | ||
| it('should return a Miss on success', async function () { | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.onlyCache, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheMiss(c, false, null); | ||
| helpers.expectCacheErrors(c, 'Error: Missing cache', 1); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataMiss(d, null, null); | ||
| helpers.expectDataErrors(d, 'Error: Missing cache', 1); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Miss); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/nocache'); | ||
| expect(c).to.have.property('error'); | ||
| expect(c.error).to.be.an(Error); | ||
| expect(c.error).to.have.property('message', 'Missing cache'); | ||
| }); | ||
| it('should return a Miss on error', async function () { | ||
| let c, d, e; | ||
| for (e = 1; e < 3; e++) { | ||
| c = await cache.go('-internal', 'cache', Cacheism.Status.onlyCache, async () => { | ||
| throw Error('cache error'); | ||
| }); | ||
| it('should return a Hit with the cached value with cache', async function () { | ||
| cache.store.data['-internal/true'] = Cacheism.Data.fromHit( | ||
| new Cacheism.Hit('-internal/true', 'cached') | ||
| ).stringify(); | ||
| helpers.expectCacheMiss(c, false, null); | ||
| helpers.expectCacheErrors(c, 'Error: Missing cache', e); | ||
| const c = await cache.go('-internal', 'true', Cacheism.Status.onlyCache, async () => { | ||
| return 'live'; | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataMiss(d, null, null); | ||
| helpers.expectDataErrors(d, 'Error: Missing cache', e); | ||
| } | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Hit); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/true'); | ||
| expect(c).to.have.property('cached', true); | ||
| expect(c).to.have.property('created'); | ||
| expect(c.created).to.be.a(Date); | ||
| expect(c).to.have.property('data', 'cached'); | ||
| expect(c).to.have.property('error', null); | ||
| expect(c).to.have.property('etag'); | ||
| }); | ||
| it('should return a Miss with no cache', async function () { | ||
| const c = await cache.go('-internal', 'nocache', Cacheism.Status.onlyCache, async () => { | ||
| return 'live'; | ||
| describe('and having existing cache', async function () { | ||
| it('should return a Hit (cached value) on success', async function () { | ||
| mockdate.set('2000-11-22'); | ||
| await cache.store.set(Cacheism.Data.fromResponse( | ||
| new Cacheism.Hit('-internal/cache', 'cached') | ||
| )); | ||
| mockdate.reset(); | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.onlyCache, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheHit(c, true, 'cached'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'cached', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| expect(c).to.be.a(Cacheism.Miss); | ||
| expect(c).to.have.property('version', 2); | ||
| expect(c).to.have.property('cacheName', '-internal/nocache'); | ||
| expect(c).to.have.property('error'); | ||
| expect(c.error).to.be.an(Error); | ||
| expect(c.error).to.have.property('message', 'Missing cache'); | ||
| it('should return a Hit (cached value) on error', async function () { | ||
| mockdate.set('2000-11-22'); | ||
| await cache.store.set(Cacheism.Data.fromResponse( | ||
| new Cacheism.Hit('-internal/cache', 'cached') | ||
| )); | ||
| mockdate.reset(); | ||
| const c = await cache.go('-internal', 'cache', Cacheism.Status.onlyCache, async () => { | ||
| return 'live'; | ||
| }); | ||
| helpers.expectCacheHit(c, true, 'cached'); | ||
| helpers.expectCacheNoErrors(c); | ||
| expect(await cache.store.isset('-internal/cache')).to.be(true); | ||
| const d = await cache.store.get('-internal/cache'); | ||
| helpers.expectDataHit(d, 'cached', c.etag); | ||
| helpers.expectDataNoErrors(d); | ||
| }); | ||
| }); | ||
@@ -263,0 +395,0 @@ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
42150
41.87%11
22.22%887
48.83%117
5.41%3
50%1
Infinity%