npm package diff

Package: @forge/cache

Versions: 1.0.3-next.0-experimental-ab129b0-experimental-3bf9516 - 1.0.3-next.0-experimental-47556b0

File: package/out/__test__/cache.test.js

Index: package/out/__test__/cache.test.js
===================================================================
--- package/out/__test__/cache.test.js
+++ package/out/__test__/cache.test.js
@@ -0,0 +1,358 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const tslib_1 = require("tslib");
+const node_fetch_1 = tslib_1.__importDefault(require("node-fetch"));
+const cache_1 = require("../cache");
+jest.mock('node-fetch');
+const fetchMock = node_fetch_1.default;
+describe('Forge Cache', () => {
+    beforeEach(() => {
+        global['__forge_runtime__'] = {
+            proxy: {
+                token: 'token',
+                url: 'https://proxy.atlassian.com'
+            },
+            rms: {
+                url: 'https://dev.services.atlassian.com',
+                host: 'rockmelon-storage.dev.atl-paas.net'
+            },
+            tracing: {
+                traceId: 'trace-id-test',
+                spanId: 'span-id-test'
+            }
+        };
+    });
+    it('chooses the v2 fetch based on runtime', async () => {
+        const cacheClient = (0, cache_1.connect)();
+        await cacheClient['client']('asdf');
+        expect(fetchMock).toHaveBeenCalled();
+    });
+});
+describe('createFetch', () => {
+    it('fetch fails when rms config is not available', async () => {
+        global['__forge_runtime__'] = {
+            proxy: {
+                token: 'token',
+                url: 'https://proxy.atlassian.com'
+            }
+        };
+        await expect((0, cache_1.createFetchRmsRuntimeV2)()).rejects.toThrowError(new Error('RMS config not available.'));
+    });
+    it('creates a fetch that adds the right headers and url', async () => {
+        global['__forge_runtime__'] = {
+            proxy: {
+                token: 'token',
+                url: 'https://proxy.atlassian.com'
+            },
+            rms: {
+                url: 'https://dev.services.atlassian.com',
+                host: 'rockmelon-storage.dev.atl-paas.net'
+            },
+            tracing: {
+                traceId: 'trace-id-test',
+                spanId: 'span-id-test'
+            }
+        };
+        const fetch = (0, cache_1.createFetchRmsRuntimeV2)();
+        await fetch('path');
+        const [absoluteUrl, options] = fetchMock.mock.lastCall ?? ['', {}];
+        expect(absoluteUrl.toString()).toEqual('https://rms/path');
+        expect((options?.agent)['passthrough']['keepAlive']).toBeTruthy();
+        expect(options?.headers).toMatchObject({
+            Authorization: 'Bearer token',
+            Host: 'rockmelon-storage.dev.atl-paas.net',
+            'x-b3-traceid': 'trace-id-test',
+            'x-b3-spanid': 'span-id-test'
+        });
+    });
+});
+describe('Cache', () => {
+    function buildCache() {
+        const fetch = jest.fn();
+        const cache = new cache_1.Cache(fetch);
+        return { cache, fetch };
+    }
+    describe('set', () => {
+        it('handles success', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({}), status: 200 });
+            await cache.set('key', 'value');
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles success with ttl', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({}), status: 200 });
+            await cache.set('key', 'value', { ttlSeconds: 100 });
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles failure', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: false, text: async () => 'Not allowed', status: 403 });
+            await expect(cache.set('key', 'value', { ttlSeconds: 100 })).rejects.toMatchError(new cache_1.ApiError(403, 'UNKNOWN_ERROR', 'Not allowed'));
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles failure with error code', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({
+                ok: false,
+                text: async () => JSON.stringify({ error: { code: 'NOT_ALLOWED', title: 'Invalid operation' } }),
+                status: 400
+            });
+            await expect(cache.set('key', 'value', { ttlSeconds: 100 })).rejects.toMatchError(new cache_1.ApiError(400, 'NOT_ALLOWED', '"title":"Invalid operation"'));
+        });
+    });
+    describe('setIfNotExists', () => {
+        it('handles success', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: 'OK' }), status: 200 });
+            const result = await cache.setIfNotExists('key', 'value');
+            expect(result).toEqual('OK');
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles success when key exists', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: null }), status: 200 });
+            const result = await cache.setIfNotExists('key', 'value');
+            expect(result).toBeNull();
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles success with ttl', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: 'OK' }), status: 200 });
+            await cache.setIfNotExists('key', 'value', { ttlSeconds: 100 });
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles failure', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: false, text: async () => 'Not allowed', status: 403 });
+            await expect(cache.setIfNotExists('key', 'value', { ttlSeconds: 100 })).rejects.toMatchError(new cache_1.ApiError(403, 'UNKNOWN_ERROR', 'Not allowed'));
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+    });
+    describe('get', () => {
+        it('handles success', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({
+                ok: true,
+                text: async () => JSON.stringify({ response: 'asdfasdf' }),
+                status: 200
+            });
+            const result = await cache.get('key');
+            expect(result).toEqual('asdfasdf');
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles success when key does not exist', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: null }), status: 200 });
+            const result = await cache.get('key');
+            expect(result).toBeNull();
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles failure', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: false, text: async () => 'Not allowed', status: 403 });
+            await expect(cache.get('key')).rejects.toMatchError(new cache_1.ApiError(403, 'UNKNOWN_ERROR', 'Not allowed'));
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+    });
+    describe('getAndSet', () => {
+        it('handles success', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({
+                ok: true,
+                text: async () => JSON.stringify({ response: 'oldValue' }),
+                status: 200
+            });
+            const result = await cache.getAndSet('key', 'newValue');
+            expect(result).toEqual('oldValue');
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles success when key does not exist', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: null }), status: 200 });
+            const result = await cache.getAndSet('key', 'value');
+            expect(result).toBeNull();
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles success with ttl', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({
+                ok: true,
+                text: async () => JSON.stringify({ response: 'oldValue' }),
+                status: 200
+            });
+            await cache.getAndSet('key', 'newValue', { ttlSeconds: 100 });
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles failure', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: false, text: async () => 'Not allowed', status: 403 });
+            await expect(cache.getAndSet('key', 'value', { ttlSeconds: 100 })).rejects.toMatchError(new cache_1.ApiError(403, 'UNKNOWN_ERROR', 'Not allowed'));
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+    });
+    describe('incrementAndGet', () => {
+        it('handles success', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: 0 }), status: 200 });
+            const result = await cache.incrementAndGet('key');
+            expect(result).toEqual(0);
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles success with ttl', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: 0 }), status: 200 });
+            const result = await cache.incrementAndGet('key', { ttlSeconds: 100 });
+            expect(result).toEqual(0);
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles success when key exists', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: 11 }), status: 200 });
+            const result = await cache.incrementAndGet('key');
+            expect(result).toEqual(11);
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles failure', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: false, text: async () => 'Not allowed', status: 403 });
+            await expect(cache.incrementAndGet('key')).rejects.toMatchError(new cache_1.ApiError(403, 'UNKNOWN_ERROR', 'Not allowed'));
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+    });
+    describe('decrementAndGet', () => {
+        it('handles success', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: 0 }), status: 200 });
+            const result = await cache.decrementAndGet('key');
+            expect(result).toEqual(0);
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles success with ttl', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: 0 }), status: 200 });
+            const result = await cache.decrementAndGet('key', { ttlSeconds: 100 });
+            expect(result).toEqual(0);
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles success when key exists', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: 11 }), status: 200 });
+            const result = await cache.decrementAndGet('key');
+            expect(result).toEqual(11);
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles failure', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: false, text: async () => 'Not allowed', status: 403 });
+            await expect(cache.decrementAndGet('key')).rejects.toMatchError(new cache_1.ApiError(403, 'UNKNOWN_ERROR', 'Not allowed'));
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+    });
+    describe('delete', () => {
+        it('handles success', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: 1 }), status: 200 });
+            const result = await cache.delete('key');
+            expect(result).toEqual(1);
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles failure', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: false, text: async () => 'Not allowed', status: 403 });
+            await expect(cache.delete('key')).rejects.toMatchError(new cache_1.ApiError(403, 'UNKNOWN_ERROR', 'Not allowed'));
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+    });
+    describe('scan', () => {
+        it('handles success', async () => {
+            const { cache, fetch } = buildCache();
+            const expectedResponse = { cursor: '0', keys: ['key1', 'key2'] };
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify(expectedResponse), status: 200 });
+            const actualResponse = await cache.scan('key*');
+            expect(actualResponse).toEqual(expectedResponse);
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles success with cursor', async () => {
+            const { cache, fetch } = buildCache();
+            const expectedResponse = { cursor: '0', keys: ['key2'] };
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify(expectedResponse), status: 200 });
+            const actualResponse = await cache.scan('key*', { cursor: '1' });
+            expect(actualResponse).toEqual(expectedResponse);
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles success with count', async () => {
+            const { cache, fetch } = buildCache();
+            const expectedResponse = { cursor: '0', keys: ['key1'] };
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify(expectedResponse), status: 200 });
+            const actualResponse = await cache.scan('key*', { count: 1 });
+            expect(actualResponse).toEqual(expectedResponse);
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles failure', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: false, text: async () => 'Not allowed', status: 403 });
+            await expect(cache.scan('key*', { cursor: '0', count: 2 })).rejects.toMatchError(new cache_1.ApiError(403, 'UNKNOWN_ERROR', 'Not allowed'));
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+    });
+    describe('leftPush', () => {
+        it('handles success', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: 1 }), status: 200 });
+            const result = await cache.leftPush('list', 'value');
+            expect(result).toEqual(1);
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles failure', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: false, text: async () => 'Not allowed', status: 403 });
+            await expect(cache.leftPush('list', 'value')).rejects.toMatchError(new cache_1.ApiError(403, 'UNKNOWN_ERROR', 'Not allowed'));
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+    });
+    describe('rightPop', () => {
+        it('handles success', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: 'value' }), status: 200 });
+            const result = await cache.rightPop('list');
+            expect(result).toEqual('value');
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles success when key does not exist', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: null }), status: 200 });
+            const result = await cache.rightPop('list');
+            expect(result).toBeNull();
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles failure', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: false, text: async () => 'Not allowed', status: 403 });
+            await expect(cache.rightPop('list')).rejects.toMatchError(new cache_1.ApiError(403, 'UNKNOWN_ERROR', 'Not allowed'));
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+    });
+    describe('listLength', () => {
+        it('handles success', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: 2 }), status: 200 });
+            const result = await cache.listLength('list');
+            expect(result).toBe(2);
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles success when key does not exist', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: true, text: async () => JSON.stringify({ response: 0 }), status: 200 });
+            const result = await cache.listLength('list');
+            expect(result).toBe(0);
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+        it('handles failure', async () => {
+            const { cache, fetch } = buildCache();
+            fetch.mockResolvedValueOnce({ ok: false, text: async () => 'Not allowed', status: 403 });
+            await expect(cache.listLength('list')).rejects.toMatchError(new cache_1.ApiError(403, 'UNKNOWN_ERROR', 'Not allowed'));
+            expect(fetch.mock.lastCall).toMatchSnapshot();
+        });
+    });
+});