瀏覽代碼

Tests for apiMethods

Kirk Trombley 4 年之前
父節點
當前提交
2bb7bb5b2b

+ 4 - 8
client/src/domain/apiMethods.js

@@ -27,15 +27,11 @@ export const checkScore = async (point1, point2) => {
 };
 
 export const getGenerators = async () => {
-  try {
-    const res = await fetch(`${API_BASE}/generators`);
-    if (!res.ok) {
-      throw Error(res.statusText);
-    }
-    return await res.json();
-  } catch (err) {
-    return { status: err.message, version: null };
+  const res = await fetch(`${API_BASE}/generators`);
+  if (!res.ok) {
+    throw Error(res.statusText);
   }
+  return res.json();
 };
 
 export const createGame = async (

+ 878 - 0
client/src/tests/__snapshots__/apiMethods.test.js.snap

@@ -0,0 +1,878 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`apiMethods checkScore checks score 1`] = `
+Object {
+  "score": 1000,
+}
+`;
+
+exports[`apiMethods checkScore checks score 2`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/score",
+      Object {
+        "body": "{\\"point1\\":\\"point1\\",\\"point2\\":\\"point2\\"}",
+        "headers": Object {
+          "Content-Type": "application/json",
+        },
+        "method": "POST",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods checkScore passes thrown error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/score",
+      Object {
+        "body": "{\\"point1\\":\\"point1\\",\\"point2\\":\\"point2\\"}",
+        "headers": Object {
+          "Content-Type": "application/json",
+        },
+        "method": "POST",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "throw",
+      "value": [Error: error],
+    },
+  ],
+}
+`;
+
+exports[`apiMethods checkScore throws on server error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/score",
+      Object {
+        "body": "{\\"point1\\":\\"point1\\",\\"point2\\":\\"point2\\"}",
+        "headers": Object {
+          "Content-Type": "application/json",
+        },
+        "method": "POST",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods createGame creates game 1`] = `"test-game-id"`;
+
+exports[`apiMethods createGame creates game 2`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game",
+      Object {
+        "body": "{\\"timer\\":\\"timer\\",\\"rounds\\":\\"rounds\\",\\"countryLock\\":\\"countryLock\\",\\"generationMethod\\":\\"generationMethod\\",\\"ruleSet\\":\\"ruleSet\\"}",
+        "headers": Object {
+          "Content-Type": "application/json",
+        },
+        "method": "PUT",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods createGame passes thrown error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game",
+      Object {
+        "body": "{\\"timer\\":\\"timer\\",\\"rounds\\":\\"rounds\\",\\"countryLock\\":\\"countryLock\\",\\"generationMethod\\":\\"generationMethod\\",\\"ruleSet\\":\\"ruleSet\\"}",
+        "headers": Object {
+          "Content-Type": "application/json",
+        },
+        "method": "PUT",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "throw",
+      "value": [Error: error],
+    },
+  ],
+}
+`;
+
+exports[`apiMethods createGame throws on server error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game",
+      Object {
+        "body": "{\\"timer\\":\\"timer\\",\\"rounds\\":\\"rounds\\",\\"countryLock\\":\\"countryLock\\",\\"generationMethod\\":\\"generationMethod\\",\\"ruleSet\\":\\"ruleSet\\"}",
+        "headers": Object {
+          "Content-Type": "application/json",
+        },
+        "method": "PUT",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getCurrentRound gets current round 1`] = `
+Object {
+  "round": "test-round",
+}
+`;
+
+exports[`apiMethods getCurrentRound gets current round 2`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/players/test-player-id/current",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getCurrentRound passes thrown error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/players/test-player-id/current",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "throw",
+      "value": [Error: error],
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getCurrentRound throws on server error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/players/test-player-id/current",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getFirstSubmitter gets first submmiter for a round 1`] = `"test-player"`;
+
+exports[`apiMethods getFirstSubmitter gets first submmiter for a round 2`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/round/round/first",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getFirstSubmitter passes thrown error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/round/round/first",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "throw",
+      "value": [Error: error],
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getFirstSubmitter returns null on server error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/round/round/first",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getGameConfig gets game config 1`] = `
+Object {
+  "config": "test-config",
+}
+`;
+
+exports[`apiMethods getGameConfig gets game config 2`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/config",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getGameConfig passes thrown error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/config",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "throw",
+      "value": [Error: error],
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getGameConfig throws on server error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/config",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getGameCoords gets game coords 1`] = `
+Object {
+  "coords": Array [
+    "test-coord",
+  ],
+}
+`;
+
+exports[`apiMethods getGameCoords gets game coords 2`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/coords",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getGameCoords passes thrown error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/coords",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "throw",
+      "value": [Error: error],
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getGameCoords throws on server error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/coords",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getGenerators gets generators 1`] = `
+Object {
+  "generators": Array [],
+}
+`;
+
+exports[`apiMethods getGenerators gets generators 2`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/generators",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getGenerators passes thrown error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/generators",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "throw",
+      "value": [Error: error],
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getGenerators throws on server error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/generators",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getLinkedGame gets linked game 1`] = `
+Array [
+  "test-linked-game-id",
+]
+`;
+
+exports[`apiMethods getLinkedGame gets linked game 2`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/linked",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getLinkedGame passes thrown error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/linked",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "throw",
+      "value": [Error: error],
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getLinkedGame throws on server error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/linked",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getPlayers gets game players 1`] = `
+Array [
+  "test-player",
+]
+`;
+
+exports[`apiMethods getPlayers gets game players 2`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/players",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getPlayers passes thrown error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/players",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "throw",
+      "value": [Error: error],
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getPlayers throws on server error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/players",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getStatus gets status 1`] = `
+Object {
+  "status": "healthy",
+  "version": "test",
+}
+`;
+
+exports[`apiMethods getStatus gets status 2`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/health",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getStatus handles server error 1`] = `
+Object {
+  "status": "failed",
+  "version": null,
+}
+`;
+
+exports[`apiMethods getStatus handles server error 2`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/health",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods getStatus handles thrown error 1`] = `
+Object {
+  "status": "error",
+  "version": null,
+}
+`;
+
+exports[`apiMethods getStatus handles thrown error 2`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/health",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "throw",
+      "value": [Error: error],
+    },
+  ],
+}
+`;
+
+exports[`apiMethods joinGame joins game 1`] = `
+Object {
+  "playerId": "test-player-id",
+}
+`;
+
+exports[`apiMethods joinGame joins game 2`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/join",
+      Object {
+        "body": "{\\"playerName\\":\\"test-player-name\\"}",
+        "headers": Object {
+          "Content-Type": "application/json",
+        },
+        "method": "POST",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods joinGame passes thrown error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/join",
+      Object {
+        "body": "{\\"playerName\\":\\"test-player-name\\"}",
+        "headers": Object {
+          "Content-Type": "application/json",
+        },
+        "method": "POST",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "throw",
+      "value": [Error: error],
+    },
+  ],
+}
+`;
+
+exports[`apiMethods joinGame throws on server error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/join",
+      Object {
+        "body": "{\\"playerName\\":\\"test-player-name\\"}",
+        "headers": Object {
+          "Content-Type": "application/json",
+        },
+        "method": "POST",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods linkGame links game to another 1`] = `undefined`;
+
+exports[`apiMethods linkGame links game to another 2`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/linked",
+      Object {
+        "body": "{\\"linkedGame\\":\\"test-linked-game-id\\"}",
+        "headers": Object {
+          "Content-Type": "application/json",
+        },
+        "method": "PUT",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods linkGame passes thrown error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/linked",
+      Object {
+        "body": "{\\"linkedGame\\":\\"test-linked-game-id\\"}",
+        "headers": Object {
+          "Content-Type": "application/json",
+        },
+        "method": "PUT",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "throw",
+      "value": [Error: error],
+    },
+  ],
+}
+`;
+
+exports[`apiMethods linkGame throws on server error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/linked",
+      Object {
+        "body": "{\\"linkedGame\\":\\"test-linked-game-id\\"}",
+        "headers": Object {
+          "Content-Type": "application/json",
+        },
+        "method": "PUT",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods sendGuess passes thrown error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/round/test-round/guess/test-player-id",
+      Object {
+        "body": "{\\"timeRemaining\\":\\"test-time\\",\\"lat\\":\\"lat\\",\\"lng\\":\\"lng\\"}",
+        "headers": Object {
+          "Content-Type": "application/json",
+        },
+        "method": "POST",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "throw",
+      "value": [Error: error],
+    },
+  ],
+}
+`;
+
+exports[`apiMethods sendGuess sends a guess 1`] = `
+Object {
+  "score": 1000,
+  "totalScore": 5000,
+}
+`;
+
+exports[`apiMethods sendGuess sends a guess 2`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/round/test-round/guess/test-player-id",
+      Object {
+        "body": "{\\"timeRemaining\\":\\"test-time\\",\\"lat\\":\\"lat\\",\\"lng\\":\\"lng\\"}",
+        "headers": Object {
+          "Content-Type": "application/json",
+        },
+        "method": "POST",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods sendGuess throws on server error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/round/test-round/guess/test-player-id",
+      Object {
+        "body": "{\\"timeRemaining\\":\\"test-time\\",\\"lat\\":\\"lat\\",\\"lng\\":\\"lng\\"}",
+        "headers": Object {
+          "Content-Type": "application/json",
+        },
+        "method": "POST",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods sendtimeout passes thrown error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/round/test-round/timeout/test-player-id",
+      Object {
+        "method": "POST",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "throw",
+      "value": [Error: error],
+    },
+  ],
+}
+`;
+
+exports[`apiMethods sendtimeout sends a timeout for the round 1`] = `
+Object {
+  "score": 0,
+  "totalScore": 2000,
+}
+`;
+
+exports[`apiMethods sendtimeout sends a timeout for the round 2`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/round/test-round/timeout/test-player-id",
+      Object {
+        "method": "POST",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;
+
+exports[`apiMethods sendtimeout throws on server error 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "https://hiram.services/terrassumptions/api/game/test-game-id/round/test-round/timeout/test-player-id",
+      Object {
+        "method": "POST",
+      },
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+  ],
+}
+`;

+ 557 - 0
client/src/tests/apiMethods.test.js

@@ -0,0 +1,557 @@
+import {
+  checkScore,
+  createGame,
+  getCurrentRound,
+  getFirstSubmitter,
+  getGameConfig,
+  getGameCoords,
+  getGenerators,
+  getLinkedGame,
+  getPlayers,
+  getStatus,
+  joinGame,
+  linkGame,
+  sendGuess,
+  sendTimeout,
+} from "../domain/apiMethods";
+
+global.fetch = jest.fn();
+
+describe("apiMethods", () => {
+  describe("getStatus", () => {
+    it("gets status", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: true,
+          json: () =>
+            Promise.resolve({
+              status: "healthy",
+              version: "test",
+            }),
+        })
+      );
+      expect(await getStatus()).toMatchSnapshot();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("handles server error", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: false,
+          statusText: "failed",
+        })
+      );
+      expect(await getStatus()).toMatchSnapshot();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("handles thrown error", async () => {
+      global.fetch.mockImplementation(() => {
+        throw new Error("error");
+      });
+      expect(await getStatus()).toMatchSnapshot();
+      expect(global.fetch).toMatchSnapshot();
+    });
+  });
+
+  describe("checkScore", () => {
+    it("checks score", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: true,
+          json: () =>
+            Promise.resolve({
+              score: 1000,
+            }),
+        })
+      );
+      expect(await checkScore("point1", "point2")).toMatchSnapshot();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("throws on server error", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: false,
+          statusText: "failed",
+        })
+      );
+      await expect(checkScore("point1", "point2")).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("passes thrown error", async () => {
+      global.fetch.mockImplementation(() => {
+        throw new Error("error");
+      });
+      await expect(checkScore("point1", "point2")).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+  });
+
+  describe("getGenerators", () => {
+    it("gets generators", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: true,
+          json: () =>
+            Promise.resolve({
+              generators: [],
+            }),
+        })
+      );
+      expect(await getGenerators()).toMatchSnapshot();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("throws on server error", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: false,
+          statusText: "failed",
+        })
+      );
+      await expect(getGenerators()).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("passes thrown error", async () => {
+      global.fetch.mockImplementation(() => {
+        throw new Error("error");
+      });
+      await expect(getGenerators()).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+  });
+
+  describe("createGame", () => {
+    it("creates game", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: true,
+          json: () =>
+            Promise.resolve({
+              gameId: "test-game-id",
+            }),
+        })
+      );
+      expect(
+        await createGame(
+          "timer",
+          "rounds",
+          "countryLock",
+          "generationMethod",
+          "ruleSet"
+        )
+      ).toMatchSnapshot();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("throws on server error", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: false,
+          statusText: "failed",
+        })
+      );
+      await expect(
+        createGame(
+          "timer",
+          "rounds",
+          "countryLock",
+          "generationMethod",
+          "ruleSet"
+        )
+      ).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("passes thrown error", async () => {
+      global.fetch.mockImplementation(() => {
+        throw new Error("error");
+      });
+      await expect(
+        createGame(
+          "timer",
+          "rounds",
+          "countryLock",
+          "generationMethod",
+          "ruleSet"
+        )
+      ).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+  });
+
+  describe("getGameConfig", () => {
+    it("gets game config", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: true,
+          json: () =>
+            Promise.resolve({
+              config: "test-config",
+            }),
+        })
+      );
+      expect(await getGameConfig("test-game-id")).toMatchSnapshot();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("throws on server error", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: false,
+          statusText: "failed",
+        })
+      );
+      await expect(getGameConfig("test-game-id")).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("passes thrown error", async () => {
+      global.fetch.mockImplementation(() => {
+        throw new Error("error");
+      });
+      await expect(getGameConfig("test-game-id")).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+  });
+
+  describe("getGameCoords", () => {
+    it("gets game coords", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: true,
+          json: () =>
+            Promise.resolve({
+              coords: ["test-coord"],
+            }),
+        })
+      );
+      expect(await getGameCoords("test-game-id")).toMatchSnapshot();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("throws on server error", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: false,
+          statusText: "failed",
+        })
+      );
+      await expect(getGameCoords("test-game-id")).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("passes thrown error", async () => {
+      global.fetch.mockImplementation(() => {
+        throw new Error("error");
+      });
+      await expect(getGameCoords("test-game-id")).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+  });
+
+  describe("getPlayers", () => {
+    it("gets game players", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: true,
+          json: () =>
+            Promise.resolve({
+              players: ["test-player"],
+            }),
+        })
+      );
+      expect(await getPlayers("test-game-id")).toMatchSnapshot();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("throws on server error", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: false,
+          statusText: "failed",
+        })
+      );
+      await expect(getPlayers("test-game-id")).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("passes thrown error", async () => {
+      global.fetch.mockImplementation(() => {
+        throw new Error("error");
+      });
+      await expect(getPlayers("test-game-id")).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+  });
+
+  describe("getLinkedGame", () => {
+    it("gets linked game", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: true,
+          json: () =>
+            Promise.resolve({
+              linkedGame: ["test-linked-game-id"],
+            }),
+        })
+      );
+      expect(await getLinkedGame("test-game-id")).toMatchSnapshot();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("throws on server error", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: false,
+          statusText: "failed",
+        })
+      );
+      await expect(getLinkedGame("test-game-id")).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("passes thrown error", async () => {
+      global.fetch.mockImplementation(() => {
+        throw new Error("error");
+      });
+      await expect(getLinkedGame("test-game-id")).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+  });
+
+  describe("linkGame", () => {
+    it("links game to another", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: true,
+        })
+      );
+      expect(
+        await linkGame("test-game-id", "test-linked-game-id")
+      ).toMatchSnapshot();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("throws on server error", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: false,
+          statusText: "failed",
+        })
+      );
+      await expect(
+        linkGame("test-game-id", "test-linked-game-id")
+      ).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("passes thrown error", async () => {
+      global.fetch.mockImplementation(() => {
+        throw new Error("error");
+      });
+      await expect(
+        linkGame("test-game-id", "test-linked-game-id")
+      ).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+  });
+
+  describe("getFirstSubmitter", () => {
+    it("gets first submmiter for a round", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: true,
+          json: () =>
+            Promise.resolve({
+              first: "test-player",
+            }),
+        })
+      );
+      expect(
+        await getFirstSubmitter("test-game-id", "round")
+      ).toMatchSnapshot();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("returns null on server error", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: false,
+          statusText: "failed",
+        })
+      );
+      expect(await getFirstSubmitter("test-game-id", "round")).toBe(null);
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("passes thrown error", async () => {
+      global.fetch.mockImplementation(() => {
+        throw new Error("error");
+      });
+      await expect(
+        getFirstSubmitter("test-game-id", "round")
+      ).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+  });
+
+  describe("joinGame", () => {
+    it("joins game", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: true,
+          json: () =>
+            Promise.resolve({
+              playerId: "test-player-id",
+            }),
+        })
+      );
+      expect(
+        await joinGame("test-game-id", "test-player-name")
+      ).toMatchSnapshot();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("throws on server error", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: false,
+          statusText: "failed",
+        })
+      );
+      await expect(
+        joinGame("test-game-id", "test-player-name")
+      ).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("passes thrown error", async () => {
+      global.fetch.mockImplementation(() => {
+        throw new Error("error");
+      });
+      await expect(
+        joinGame("test-game-id", "test-player-name")
+      ).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+  });
+
+  describe("getCurrentRound", () => {
+    it("gets current round", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: true,
+          json: () =>
+            Promise.resolve({
+              round: "test-round",
+            }),
+        })
+      );
+      expect(
+        await getCurrentRound("test-game-id", "test-player-id")
+      ).toMatchSnapshot();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("throws on server error", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: false,
+          statusText: "failed",
+        })
+      );
+      await expect(
+        getCurrentRound("test-game-id", "test-player-id")
+      ).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("passes thrown error", async () => {
+      global.fetch.mockImplementation(() => {
+        throw new Error("error");
+      });
+      await expect(
+        getCurrentRound("test-game-id", "test-player-id")
+      ).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+  });
+
+  describe("sendGuess", () => {
+    it("sends a guess", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: true,
+          json: () =>
+            Promise.resolve({
+              score: 1000,
+              totalScore: 5000,
+            }),
+        })
+      );
+      expect(
+        await sendGuess(
+          "test-game-id",
+          "test-player-id",
+          "test-round",
+          { lat: "lat", lng: "lng" },
+          "test-time"
+        )
+      ).toMatchSnapshot();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("throws on server error", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: false,
+          statusText: "failed",
+        })
+      );
+      await expect(
+        sendGuess(
+          "test-game-id",
+          "test-player-id",
+          "test-round",
+          { lat: "lat", lng: "lng" },
+          "test-time"
+        )
+      ).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("passes thrown error", async () => {
+      global.fetch.mockImplementation(() => {
+        throw new Error("error");
+      });
+      await expect(
+        sendGuess(
+          "test-game-id",
+          "test-player-id",
+          "test-round",
+          { lat: "lat", lng: "lng" },
+          "test-time"
+        )
+      ).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+  });
+
+  describe("sendtimeout", () => {
+    it("sends a timeout for the round", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: true,
+          json: () =>
+            Promise.resolve({
+              score: 0,
+              totalScore: 2000,
+            }),
+        })
+      );
+      expect(
+        await sendTimeout("test-game-id", "test-player-id", "test-round")
+      ).toMatchSnapshot();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("throws on server error", async () => {
+      global.fetch.mockImplementation(() =>
+        Promise.resolve({
+          ok: false,
+          statusText: "failed",
+        })
+      );
+      await expect(
+        sendTimeout("test-game-id", "test-player-id", "test-round")
+      ).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+    it("passes thrown error", async () => {
+      global.fetch.mockImplementation(() => {
+        throw new Error("error");
+      });
+      await expect(
+        sendTimeout("test-game-id", "test-player-id", "test-round")
+      ).rejects.toThrow();
+      expect(global.fetch).toMatchSnapshot();
+    });
+  });
+});