<NME> RelayQueryResponseCache-test.js
<BEF> /**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @format
 * @oncall relay
 */

'use strict';

const RelayQueryResponseCache = require('../RelayQueryResponseCache');

describe('RelayQueryResponseCache', () => {
  let dateNow;
  let queryID;

  beforeEach(() => {
    jest.resetModules();

    dateNow = Date.now;
    queryID = '<query-id>';
  });

  afterEach(() => {
    Date.now = dateNow;
  });

  describe('clear()', () => {
    it('clears entries from the cache', () => {
      const cache = new RelayQueryResponseCache({size: 1, ttl: 1000});
      cache.set(queryID, {id: 1}, {});
      cache.clear();
      expect(cache.get(queryID, {id: 1})).toBe(null);
    });
  });

  describe('get()', () => {
    it('returns known entries', () => {
  describe('get()', () => {
    it('returns known entries', () => {
      const cache = new RelayQueryResponseCache({size: 1, ttl: 1000});
      const result = {};
      const variables = {id: 1};
      cache.set(queryID, variables, result);
      expect(cache.get(queryID, variables)).toBe(result);
    });

    it('returns null for unknown entries', () => {
      expect(cache.get(queryID, {id: 2})).toBe(null);
    });

    it('expires entries', () => {

    it('expires entries', () => {
      const cache = new RelayQueryResponseCache({size: 1, ttl: 9});
      const result = {};
      const variables = {id: 1};
      Date.now = () => 0;
      cache.set(queryID, variables, result);
      Date.now = () => 9;
      expect(cache.get(queryID, variables)).toBe(result);
      Date.now = () => 10;
      expect(cache.get(queryID, variables)).toBe(null);
    });
  });
      const payload = [{}, {}];
      const variables = {id: 1};
      const result = [
        {extensions: {cacheTimestamp: 5}},
        {extensions: {cacheTimestamp: 5}},
      ];
      Date.now = () => 5;
      cache.set(queryID, variables, payload);
      expect(cache.get(queryID, {id: 1})).toBe(null);
      expect(cache.get(queryID, {id: 2})).not.toBe(null);

      cache.set(queryID, {id: 1});
      expect(cache.get(queryID, {id: 2})).toBe(null);
      expect(cache.get(queryID, {id: 1})).not.toBe(null);
    });
      cache.set(queryID, {id: 1}, {});
      expect(cache.get(queryID, {id: 1})).not.toBe(null);

      cache.set(queryID, {id: 2}, {});
      expect(cache.get(queryID, {id: 1})).toBe(null);
      expect(cache.get(queryID, {id: 2})).not.toBe(null);

      cache.set(queryID, {id: 1}, {});
      expect(cache.get(queryID, {id: 2})).toBe(null);
      expect(cache.get(queryID, {id: 1})).not.toBe(null);
    });
  });
});

<MSG> Include cache timestamp into GraphQLResponse extensions

Reviewed By: josephsavona

Differential Revision: D13169269

fbshipit-source-id: fc2de2391b27177badbba50d468d07a7e70b320c

<DFF> @@ -41,10 +41,12 @@ describe('RelayQueryResponseCache', () => {
   describe('get()', () => {
     it('returns known entries', () => {
       const cache = new RelayQueryResponseCache({size: 1, ttl: 1000});
-      const result = {};
+      const payload = {};
       const variables = {id: 1};
-      cache.set(queryID, variables, result);
-      expect(cache.get(queryID, variables)).toBe(result);
+      const result = {...payload, extensions: {cacheTimestamp: 5}};
+      Date.now = () => 5;
+      cache.set(queryID, variables, payload);
+      expect(cache.get(queryID, variables)).toEqual(result);
     });
 
     it('returns null for unknown entries', () => {
@@ -55,13 +57,14 @@ describe('RelayQueryResponseCache', () => {
 
     it('expires entries', () => {
       const cache = new RelayQueryResponseCache({size: 1, ttl: 9});
-      const result = {};
+      const payload = {};
       const variables = {id: 1};
-      Date.now = () => 0;
-      cache.set(queryID, variables, result);
-      Date.now = () => 9;
-      expect(cache.get(queryID, variables)).toBe(result);
+      const result = {...payload, extensions: {cacheTimestamp: 1}};
+      Date.now = () => 1;
+      cache.set(queryID, variables, payload);
       Date.now = () => 10;
+      expect(cache.get(queryID, variables)).toEqual(result);
+      Date.now = () => 11;
       expect(cache.get(queryID, variables)).toBe(null);
     });
   });
@@ -76,7 +79,7 @@ describe('RelayQueryResponseCache', () => {
       expect(cache.get(queryID, {id: 1})).toBe(null);
       expect(cache.get(queryID, {id: 2})).not.toBe(null);
 
-      cache.set(queryID, {id: 1});
+      cache.set(queryID, {id: 1}, {});
       expect(cache.get(queryID, {id: 2})).toBe(null);
       expect(cache.get(queryID, {id: 1})).not.toBe(null);
     });

{'commit': "diff --git a/packages/relay-runtime/network/RelayQueryResponseCache.js b/packages/relay-runtime/network/RelayQueryResponseCache.js\nindex 8e863bcb65e6..08527bec000f 100644\n--- a/packages/relay-runtime/network/RelayQueryResponseCache.js\n+++ b/packages/relay-runtime/network/RelayQueryResponseCache.js\n@@ -60,7 +60,16 @@ class RelayQueryResponseCache {\n       }\n     });\n     const response = this._responses.get(cacheKey);\n-    return response != null ? response.payload : null;\n+    return response != null\n+      ? // $FlowFixMe\n+        ({\n+          ...response.payload,\n+          extensions: {\n+            ...response.payload.extensions,\n+            cacheTimestamp: response.fetchTime,\n+          },\n+        }: GraphQLResponse)\n+      : null;\n   }\n \n   set(queryID: string, variables: Variables, payload: GraphQLResponse): void {\ndiff --git a/packages/relay-runtime/network/__tests__/RelayQueryResponseCache-test.js b/packages/relay-runtime/network/__tests__/RelayQueryResponseCache-test.js\nindex 790a48c69962..28565e55b585 100644\n--- a/packages/relay-runtime/network/__tests__/RelayQueryResponseCache-test.js\n+++ b/packages/relay-runtime/network/__tests__/RelayQueryResponseCache-test.js\n@@ -41,10 +41,12 @@ describe('RelayQueryResponseCache', () => {\n   describe('get()', () => {\n     it('returns known entries', () => {\n       const cache = new RelayQueryResponseCache({size: 1, ttl: 1000});\n-      const result = {};\n+      const payload = {};\n       const variables = {id: 1};\n-      cache.set(queryID, variables, result);\n-      expect(cache.get(queryID, variables)).toBe(result);\n+      const result = {...payload, extensions: {cacheTimestamp: 5}};\n+      Date.now = () => 5;\n+      cache.set(queryID, variables, payload);\n+      expect(cache.get(queryID, variables)).toEqual(result);\n     });\n \n     it('returns null for unknown entries', () => {\n@@ -55,13 +57,14 @@ describe('RelayQueryResponseCache', () => {\n \n     it('expires entries', () => {\n       const cache = new RelayQueryResponseCache({size: 1, ttl: 9});\n-      const result = {};\n+      const payload = {};\n       const variables = {id: 1};\n-      Date.now = () => 0;\n-      cache.set(queryID, variables, result);\n-      Date.now = () => 9;\n-      expect(cache.get(queryID, variables)).toBe(result);\n+      const result = {...payload, extensions: {cacheTimestamp: 1}};\n+      Date.now = () => 1;\n+      cache.set(queryID, variables, payload);\n       Date.now = () => 10;\n+      expect(cache.get(queryID, variables)).toEqual(result);\n+      Date.now = () => 11;\n       expect(cache.get(queryID, variables)).toBe(null);\n     });\n   });\n@@ -76,7 +79,7 @@ describe('RelayQueryResponseCache', () => {\n       expect(cache.get(queryID, {id: 1})).toBe(null);\n       expect(cache.get(queryID, {id: 2})).not.toBe(null);\n \n-      cache.set(queryID, {id: 1});\n+      cache.set(queryID, {id: 1}, {});\n       expect(cache.get(queryID, {id: 2})).toBe(null);\n       expect(cache.get(queryID, {id: 1})).not.toBe(null);\n     });\n", 'commit_subject': 'Include cache timestamp into GraphQLResponse extensions', 'repo_name': 'facebook/relay', 'committer': {'name': 'Facebook Github Bot', 'email': 'f08905f3496a7b7d60e0da97307f6ad7594abc92@users.noreply.github.com', 'time_sec': '1544095548', 'tz_offset': '-480', 'date': {'seconds': '1544095548'}}, 'commit_tree': 'c24aa920a5e70a01ad1e7476770bc741874478cb', 'commit_message': 'Include cache timestamp into GraphQLResponse extensions\n\nReviewed By: josephsavona\n\nDifferential Revision: D13169269\n\nfbshipit-source-id: fc2de2391b27177badbba50d468d07a7e70b320c\n', 'language_name': 'Rust', 'license': 'mit', 'hunks': "@@ -41,10 +41,12 @@ describe('RelayQueryResponseCache', () => {\n   describe('get()', () => {\n     it('returns known entries', () => {\n       const cache = new RelayQueryResponseCache({size: 1, ttl: 1000});\n-      const result = {};\n+      const payload = {};\n       const variables = {id: 1};\n-      cache.set(queryID, variables, result);\n-      expect(cache.get(queryID, variables)).toBe(result);\n+      const result = {...payload, extensions: {cacheTimestamp: 5}};\n+      Date.now = () => 5;\n+      cache.set(queryID, variables, payload);\n+      expect(cache.get(queryID, variables)).toEqual(result);\n     });\n \n     it('returns null for unknown entries', () => {\n@@ -55,13 +57,14 @@ describe('RelayQueryResponseCache', () => {\n \n     it('expires entries', () => {\n       const cache = new RelayQueryResponseCache({size: 1, ttl: 9});\n-      const result = {};\n+      const payload = {};\n       const variables = {id: 1};\n-      Date.now = () => 0;\n-      cache.set(queryID, variables, result);\n-      Date.now = () => 9;\n-      expect(cache.get(queryID, variables)).toBe(result);\n+      const result = {...payload, extensions: {cacheTimestamp: 1}};\n+      Date.now = () => 1;\n+      cache.set(queryID, variables, payload);\n       Date.now = () => 10;\n+      expect(cache.get(queryID, variables)).toEqual(result);\n+      Date.now = () => 11;\n       expect(cache.get(queryID, variables)).toBe(null);\n     });\n   });\n@@ -76,7 +79,7 @@ describe('RelayQueryResponseCache', () => {\n       expect(cache.get(queryID, {id: 1})).toBe(null);\n       expect(cache.get(queryID, {id: 2})).not.toBe(null);\n \n-      cache.set(queryID, {id: 1});\n+      cache.set(queryID, {id: 1}, {});\n       expect(cache.get(queryID, {id: 2})).toBe(null);\n       expect(cache.get(queryID, {id: 1})).not.toBe(null);\n     });\n", 'addition_count': 12, 'deletion_count': 9, 'src_file': 'a/packages/relay-runtime/network/__tests__/RelayQueryResponseCache-test.js', 'tgt_file': 'b/packages/relay-runtime/network/__tests__/RelayQueryResponseCache-test.js', 'file_extension': '.js', 'before_file': "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * @format\n * @oncall relay\n */\n\n'use strict';\n\nconst RelayQueryResponseCache = require('../RelayQueryResponseCache');\n\ndescribe('RelayQueryResponseCache', () => {\n  let dateNow;\n  let queryID;\n\n  beforeEach(() => {\n    jest.resetModules();\n\n    dateNow = Date.now;\n    queryID = '<query-id>';\n  });\n\n  afterEach(() => {\n    Date.now = dateNow;\n  });\n\n  describe('clear()', () => {\n    it('clears entries from the cache', () => {\n      const cache = new RelayQueryResponseCache({size: 1, ttl: 1000});\n      cache.set(queryID, {id: 1}, {});\n      cache.clear();\n      expect(cache.get(queryID, {id: 1})).toBe(null);\n    });\n  });\n\n  describe('get()', () => {\n    it('returns known entries', () => {\n  describe('get()', () => {\n    it('returns known entries', () => {\n      const cache = new RelayQueryResponseCache({size: 1, ttl: 1000});\n      const result = {};\n      const variables = {id: 1};\n      cache.set(queryID, variables, result);\n      expect(cache.get(queryID, variables)).toBe(result);\n    });\n\n    it('returns null for unknown entries', () => {\n      expect(cache.get(queryID, {id: 2})).toBe(null);\n    });\n\n    it('expires entries', () => {\n\n    it('expires entries', () => {\n      const cache = new RelayQueryResponseCache({size: 1, ttl: 9});\n      const result = {};\n      const variables = {id: 1};\n      Date.now = () => 0;\n      cache.set(queryID, variables, result);\n      Date.now = () => 9;\n      expect(cache.get(queryID, variables)).toBe(result);\n      Date.now = () => 10;\n      expect(cache.get(queryID, variables)).toBe(null);\n    });\n  });\n      const payload = [{}, {}];\n      const variables = {id: 1};\n      const result = [\n        {extensions: {cacheTimestamp: 5}},\n        {extensions: {cacheTimestamp: 5}},\n      ];\n      Date.now = () => 5;\n      cache.set(queryID, variables, payload);\n      expect(cache.get(queryID, {id: 1})).toBe(null);\n      expect(cache.get(queryID, {id: 2})).not.toBe(null);\n\n      cache.set(queryID, {id: 1});\n      expect(cache.get(queryID, {id: 2})).toBe(null);\n      expect(cache.get(queryID, {id: 1})).not.toBe(null);\n    });\n      cache.set(queryID, {id: 1}, {});\n      expect(cache.get(queryID, {id: 1})).not.toBe(null);\n\n      cache.set(queryID, {id: 2}, {});\n      expect(cache.get(queryID, {id: 1})).toBe(null);\n      expect(cache.get(queryID, {id: 2})).not.toBe(null);\n\n      cache.set(queryID, {id: 1}, {});\n      expect(cache.get(queryID, {id: 2})).toBe(null);\n      expect(cache.get(queryID, {id: 1})).not.toBe(null);\n    });\n  });\n});\n", 'message': 'Include cache timestamp into GraphQLResponse extensions\n\nReviewed By: josephsavona\n\nDifferential Revision: D13169269\n\nfbshipit-source-id: fc2de2391b27177badbba50d468d07a7e70b320c\n', 'author_name': 'Facebook Github Bot', 'lang': 'js'}