From d8f968bd4096de4c0da88798e0f08cdac4eecb16 Mon Sep 17 00:00:00 2001
From: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
Date: Wed, 20 Jul 2022 15:35:13 +0200
Subject: [PATCH] Refactor `backend.js` #5 - Simplify the main logic (#24119)

* Simplify temporary DB path

* Set the port to 4000

* Move logic to create server config inside the base function

* Rename `kill` function to distinguish between it and Node process `kill`

* `createServer` as a method

* Remove reliance on Maps

* Stop server logic implemented directly in a method

* Remove unneeded parameter

* Start server logic implemented directly in a method

* Remove `isReady` from global scope

* Remove `delay` from the global scope

* Rename `BackendResource` to `CypressBackend`

* Rename file

* Simplify `CypressBackend`

No need to invoke the function when we can store everything
as method definitions under `CypressBackend` object.

* Pass `port` as an argument with default value

* Use template literals for logging

* Destructure `response`

* Fix small typo in the comment

* Tidy up `dbFile` in the `server`
---
 .../{backend.js => cypress-runner-backend.js} | 127 +++++-------------
 frontend/test/__runner__/run_cypress_tests.js |   8 +-
 2 files changed, 36 insertions(+), 99 deletions(-)
 rename frontend/test/__runner__/{backend.js => cypress-runner-backend.js} (52%)

diff --git a/frontend/test/__runner__/backend.js b/frontend/test/__runner__/cypress-runner-backend.js
similarity index 52%
rename from frontend/test/__runner__/backend.js
rename to frontend/test/__runner__/cypress-runner-backend.js
index a42d896623a..1759e6db9a9 100644
--- a/frontend/test/__runner__/backend.js
+++ b/frontend/test/__runner__/cypress-runner-backend.js
@@ -7,32 +7,21 @@ const { spawn } = require("child_process");
 
 const fetch = require("isomorphic-fetch");
 
-let testDbId = 0;
-const generateTempDbPath = () =>
-  path.join(os.tmpdir(), `metabase-test-${process.pid}-${testDbId++}.db`);
+const CypressBackend = {
+  createServer(port = 4000) {
+    const generateTempDbPath = () =>
+      path.join(os.tmpdir(), `metabase-test-${process.pid}.db`);
 
-let port = 4000;
-const getPort = () => port++;
-
-const BackendResource = createSharedResource("BackendResource", {
-  create({ dbKey }) {
-    const dbFile = generateTempDbPath();
-    const absoluteDbKey = dbKey ? __dirname + dbKey : dbFile;
-    const port = getPort();
-
-    return {
-      dbKey: absoluteDbKey,
-      dbFile: dbFile,
+    const server = {
+      dbFile: generateTempDbPath(),
       host: `http://localhost:${port}`,
-      port: port,
+      port,
     };
+
+    return server;
   },
   async start(server) {
     if (!server.process) {
-      if (server.dbKey !== server.dbFile) {
-        fs.copyFileSync(`${server.dbKey}.mv.db`, `${server.dbFile}.mv.db`);
-      }
-
       const javaFlags = [
         "-XX:+IgnoreUnrecognizedVMOptions", // ignore options not recognized by this Java version (e.g. Java 8 should ignore Java 9 options)
         "-Dh2.bindAddress=localhost", // fix H2 randomly not working (?)
@@ -95,32 +84,43 @@ const BackendResource = createSharedResource("BackendResource", {
         },
       );
     }
+
     if (!(await isReady(server.host))) {
       process.stdout.write(
-        "Waiting for backend (host=" +
-          server.host +
-          " dbKey=" +
-          server.dbKey +
-          ")",
+        `Waiting for backend (host=${server.host}, dbFile=${server.dbFile})`,
       );
       while (!(await isReady(server.host))) {
         if (!process.env["CI"]) {
-          // disable for CI since it break's CircleCI's no_output_timeout
+          // disable for CI since it breaks CircleCI's no_output_timeout
           process.stdout.write(".");
         }
         await delay(500);
       }
       process.stdout.write("\n");
     }
-    console.log(
-      "Backend ready (host=" + server.host + " dbKey=" + server.dbKey + ")",
-    );
+
+    console.log(`Backend ready host=${server.host}, dbFile=${server.dbFile}`);
+
+    // Copied here from `frontend/src/metabase/lib/promise.js` to decouple Cypress from Typescript
+    function delay(duration) {
+      return new Promise((resolve, reject) => setTimeout(resolve, duration));
+    }
+
+    async function isReady(host) {
+      try {
+        const { status } = await fetch(`${host}/api/health`);
+        if (status === 200) {
+          return true;
+        }
+      } catch (e) {}
+      return false;
+    }
   },
   async stop(server) {
     if (server.process) {
       server.process.kill("SIGKILL");
       console.log(
-        "Stopped backend (host=" + server.host + " dbKey=" + server.dbKey + ")",
+        `Stopped backend (host=${server.host}, dbFile=${server.dbFile})`,
       );
     }
     try {
@@ -129,69 +129,6 @@ const BackendResource = createSharedResource("BackendResource", {
       }
     } catch (e) {}
   },
-});
-
-async function isReady(host) {
-  try {
-    const response = await fetch(`${host}/api/health`);
-    if (response.status === 200) {
-      return true;
-    }
-  } catch (e) {}
-  return false;
-}
-
-function createSharedResource(
-  resourceName,
-  { defaultOptions, create, start, stop },
-) {
-  const entriesByKey = new Map();
-  const entriesByResource = new Map();
-
-  function kill(entry) {
-    if (entriesByKey.has(entry.key)) {
-      entriesByKey.delete(entry.key);
-      entriesByResource.delete(entry.resource);
-      const p = stop(entry.resource).then(null, err =>
-        console.log("Error stopping resource", resourceName, entry.key, err),
-      );
-      return p;
-    }
-  }
-
-  return {
-    get(options = defaultOptions) {
-      const dbKey = options;
-      const key = dbKey || {};
-      let entry = entriesByKey.get(key);
-      if (!entry) {
-        entry = {
-          key: key,
-          references: 0,
-          resource: create(options),
-        };
-        entriesByKey.set(entry.key, entry);
-        entriesByResource.set(entry.resource, entry);
-      }
-      ++entry.references;
-      return entry.resource;
-    },
-    async start(resource) {
-      const entry = entriesByResource.get(resource);
-      return start(entry.resource);
-    },
-    async stop(resource) {
-      const entry = entriesByResource.get(resource);
-      if (entry && --entry.references <= 0) {
-        await kill(entry);
-      }
-    },
-  };
-}
-
-// Copied here from `frontend/src/metabase/lib/promise.js` to decouple Cypress from Typescript
-function delay(duration) {
-  return new Promise((resolve, reject) => setTimeout(resolve, duration));
-}
+};
 
-module.exports = BackendResource;
+module.exports = CypressBackend;
diff --git a/frontend/test/__runner__/run_cypress_tests.js b/frontend/test/__runner__/run_cypress_tests.js
index 57fe23588de..00007475b91 100644
--- a/frontend/test/__runner__/run_cypress_tests.js
+++ b/frontend/test/__runner__/run_cypress_tests.js
@@ -2,11 +2,11 @@ const { printBold } = require("./cypress-runner-utils");
 const runCypress = require("./cypress-runner-run-tests");
 const getVersion = require("./cypress-runner-get-version");
 const generateSnapshots = require("./cypress-runner-generate-snapshots");
-const BackendResource = require("./backend.js");
+const CypressBackend = require("./cypress-runner-backend");
 
 const e2eHost = process.env["E2E_HOST"];
 
-const server = BackendResource.get({ dbKey: "" });
+const server = CypressBackend.createServer();
 const baseUrl = e2eHost || server.host;
 
 const init = async () => {
@@ -15,7 +15,7 @@ const init = async () => {
     await getVersion();
 
     printBold("Starting backend");
-    await BackendResource.start(server);
+    await CypressBackend.start(server);
 
     printBold("Generating snapshots");
     await generateSnapshots(baseUrl, cleanup);
@@ -28,7 +28,7 @@ const init = async () => {
 const cleanup = async (exitCode = 0) => {
   if (!e2eHost) {
     printBold("Cleaning up...");
-    await BackendResource.stop(server);
+    await CypressBackend.stop(server);
   }
 
   process.exit(exitCode);
-- 
GitLab