"use strict"; import { Team } from "/models/round.js"; import Session from "/models/session.js"; import WbDb from "/data/db.js"; import SessionRepo from "/data/session_repo.js"; /** The instance used for the current test. * * Put test instances of `WbDb` into this variable. If it is used, the * connection is automatically closed after the test is done. That in turn * means that the database can then be deleted immediately, thus speeding up * the next test. * * @type {WbDb} */ let inst = null; /** Wait for the `WbDb.EVENT_CHANGE` to be fired on `instance`. * * Uses the `inst` global variable if no `instance` parameter is passed. * * @param {WbDb=} instance The instance to wait on. */ function waitForOpen(instance) { return new Promise(function(resolve) { (instance ?? inst).addEventListener(WbDb.EVENT_CHANGE, function() { if ((instance ?? inst).open) resolve(); }); }); } export default function() { QUnit.module("session_repo", function(hooks) { // delete test database before each test hooks.beforeEach(function() { return new Promise(function(resolve) { let req = indexedDB.deleteDatabase(WbDb.DB_NAME_TEST); req.onsuccess = function() { resolve(); }; }); }); // close db after each test hooks.afterEach(function() { if (inst !== null) if (inst.open) inst.db.close(); inst = null; }); QUnit.test("cannot call constructor", function(assert) { assert.throws( function() { new SessionRepo(); }, new TypeError("SessionRepo cannot be constructed")); }); QUnit.test("initially no sessions", async function(assert) { inst = WbDb.get(true); await waitForOpen(inst); let sessions = await SessionRepo.getAll(inst); assert.strictEqual(sessions.length, 0, "no sessions"); }); QUnit.test("store single session", async function(assert) { inst = WbDb.get(true); let req = waitForOpen(inst); let session = new Session(); session.ourTeam = "This is us!"; session.theirTeam = "This is them!"; session.goal = 2; session.anotherGame(); session.currentGame.currentRound.winner = Team.We; session.anotherGame(); assert.strictEqual(session.id, null, "no initial session id"); await req; let id = await SessionRepo.put(session, inst); assert.strictEqual(session.id, id, "session id has been updated"); let stored = await SessionRepo.get(id, inst); assert.deepEqual( stored.toStruct(), session.toStruct(), "sessions match"); }); QUnit.test("store two sessions", async function(assert) { inst = WbDb.get(true); let req = waitForOpen(inst); let first = new Session(); first.ourTeam = "Team A"; first.theirTeam = "Team 1"; first.goal = 2; first.anotherGame(); first.currentGame.currentRound.winner = Team.We; first.anotherGame(); let second = new Session(); second.ourTeam = "Team B"; second.theirTeam = "Team 2"; second.goal = 3; second.anotherGame(); second.currentGame.currentRound.raise(Team.We); second.currentGame.currentRound.winner = Team.They; await req; let putFirst = SessionRepo.put(first, inst); let putSecond = SessionRepo.put(second, inst); await Promise.all([putFirst, putSecond]); let sessions = await SessionRepo.getAll(inst); assert.strictEqual(sessions.length, 2, "two sessions stored"); assert.notStrictEqual(sessions[0].id, sessions[1].id, "IDs don't match"); for (let session of sessions) { let expected = null; if (session.id === first.id) { expected = first.toStruct(); } else if (session.id === second.id) { expected = second.toStruct(); } assert.deepEqual(session.toStruct(), expected, "sessions match"); } }); QUnit.test("new session reacts to changes", async function(assert) { inst = WbDb.get(true); await waitForOpen(inst); let session = new Session(); let tsInitial = session.updated; let id = await SessionRepo.put(session, inst); assert.strictEqual(session.id, id, "session id has been updated"); // ensure the timestamp is no longer the same await new Promise((resolve) => setTimeout(resolve, 1)); session.ourTeam = "This is us!"; session.theirTeam = "This is them!"; session.goal = 2; session.anotherGame(); session.currentGame.currentRound.winner = Team.We; assert.true(session.updated > tsInitial, "updated timestamp is higher"); // give the change events a chance to execute await new Promise((resolve) => setTimeout(resolve, 10)); let sessions = await SessionRepo.getAll(inst); assert.strictEqual(sessions.length, 1, "exactly one stored session"); assert.deepEqual( sessions[0].toStruct(), session.toStruct(), "sessions match"); }); QUnit.test("get sessions by age", async function(assert) { inst = WbDb.get(true); let req = waitForOpen(inst); let first = new Session(); first.ourTeam = "Team A"; first.theirTeam = "Team 1"; first.goal = 2; first.anotherGame(); first.currentGame.currentRound.winner = Team.We; first.anotherGame(); // ensure the timestamp is no longer the same await new Promise((resolve) => setTimeout(resolve, 1)); let second = new Session(); second.ourTeam = "Team B"; second.theirTeam = "Team 2"; second.goal = 3; second.anotherGame(); second.currentGame.currentRound.raise(Team.We); second.currentGame.currentRound.winner = Team.They; await req; await Promise.all([ SessionRepo.put(first, inst), SessionRepo.put(second, inst), new Promise((resolve) => setTimeout(resolve, 1)), ]); // check after initial storage let sessions = await SessionRepo.getAllFromNewest(inst); assert.strictEqual(sessions.length, 2, "exactly two sessions stored"); assert.strictEqual(sessions[0].id, second.id, "second is newer"); assert.strictEqual(sessions[1].id, first.id, "first is older"); // change older session first.currentGame.currentRound.winner = Team.They; first.anotherGame(); // give the change events a chance to execute await new Promise((resolve) => setTimeout(resolve, 10)); // check whether order has changed sessions = await SessionRepo.getAllFromNewest(inst); assert.strictEqual(sessions.length, 2, "exactly two sessions stored"); assert.strictEqual(sessions[0].id, first.id, "first is newer"); assert.strictEqual(sessions[1].id, second.id, "second is older"); }); QUnit.test("reinserting all sessions", async function(assert) { // old structurized session (see v1 tests of session model) const old = { goal: 3, ourTeam: "", theirTeam: "", games: [], currentGame: null, }; // prepare db inst = WbDb.get(true); await waitForOpen(inst); // manually insert old session (deliberately impossible with SessionRepo) let trans = inst.db.transaction([WbDb.OS_SESSIONS], "readwrite"); let os = trans.objectStore(WbDb.OS_SESSIONS); await new Promise((resolve) => os.put(old).onsuccess = resolve); // check that the session has not yet been updated let sessions = (await new Promise(function (resolve) { inst .db .transaction([WbDb.OS_SESSIONS], "readonly") .objectStore(WbDb.OS_SESSIONS) .index(WbDb.IDX_SESSIONS_UPDATED) .getAll() .onsuccess = resolve; })).target.result; assert.strictEqual(sessions.length, 0, "no session in update index"); // now update the old seession SessionRepo.reinsertAll(inst); // give the reinsertion a chance to execute await new Promise((resolve) => setTimeout(resolve, 10)); // check that the session has been updated sessions = (await new Promise(function (resolve) { inst .db .transaction([WbDb.OS_SESSIONS], "readonly") .objectStore(WbDb.OS_SESSIONS) .index(WbDb.IDX_SESSIONS_UPDATED) .getAll() .onsuccess = resolve; })).target.result; assert.strictEqual(sessions.length, 1, "session found by update index"); // Note that the inserted session data is older than the `updated` field // in the model class. Thus it being present in the index proves that the // session has indeed been parsed and reinserted. // // Also note that the exact parsing and default value adding is already // checked in the model tests, thus it would be a duplicate to test that // here too. }); }); }