This repository has been archived on 2025-11-02. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
parandum/test/functions.test.js

2185 lines
58 KiB
JavaScript

const test = require('firebase-functions-test')({
databaseURL: 'https://parandum-learning-dev.firebaseio.com',
storageBucket: 'parandum-learning-dev.appspot.com',
projectId: 'parandum-learning-dev',
}, '_private_stuff/parandum-learning-dev-private-key.json');
const admin = require("firebase-admin");
const cloudFunctions = require('../functions/index.js');
const firebase = require("@firebase/rules-unit-testing");
const hamjest = require("hamjest");
const assert = require("assert");
admin.initializeApp();
const firestore = admin.firestore();
const userOne = "M3JPrFRH6Fdo8XMUbF0l2zVZUCH3";
const userTwo = "user_02";
const setOne = "set_01";
const setTwo = "set_02";
const vocabOne = "vocab_01";
const termOne = "term_01";
const definitionOne = "definition_01";
const definitionOneTypoOne = "ddefinition_01";
const definitionOneTypoTwo = "dinition_01";
const definitionOneTypoThree = "dinition_02";
const definitionOneTypoFour = "dinition_";
const shortDefinitionOne = "d1";
const shortDefinitionOneTypoOne = "f1";
const shortDefinitionOneTypoTwo = "f2";
const soundOne = true;
const vocabTwo = "vocab_02";
const termTwo = "term_02";
const definitionTwo = "definition_02";
const soundTwo = true;
const groupOne = "group_01";
const groupTwo = "group_02";
const doubleDefinitionOne = "definition/01";
const doubleDefinitionTwo = "definition/02";
const punctuationDefinitionOne = "definition .,()-_'\"01";
const progressVocabOne = userOne + "__" + vocabOne;
const progressVocabTwo = userOne + "__" + vocabTwo;
const vocabThree = "vocab_03";
const vocabFour = "vocab_04";
const progressVocabThree = userOne + "__" + vocabThree;
const progressVocabFour = userOne + "__" + vocabFour;
const incorrectAnswer = "incorrect";
async function deleteCollection(db, collectionPath, batchSize) {
const collectionRef = db.collection(collectionPath);
const query = collectionRef.orderBy('__name__').limit(batchSize);
return new Promise((resolve, reject) => {
deleteQueryBatch(db, query, resolve).catch(reject);
});
}
async function deleteQueryBatch(db, query, resolve) {
const snapshot = await query.get();
const batchSize = snapshot.size;
if (batchSize === 0) {
// When there are no documents left, we are done
resolve();
return;
}
// Delete documents in a batch
const batch = db.batch();
snapshot.docs.forEach((doc) => {
batch.delete(doc.ref);
});
await batch.commit();
// Recurse on the next process tick, to avoid
// exploding the stack.
process.nextTick(() => {
deleteQueryBatch(db, query, resolve);
});
}
describe("Parandum Cloud Functions", function () {
this.timeout(5000);
it("Can write & delete to/from online database", async () => {
firebase.assertSucceeds(
firestore.collection("testCollection").doc("testDoc").set({
"one": "1",
"two": "2",
})
);
firebase.assertSucceeds(
firestore.collection("testCollection").doc("testDoc").delete()
);
});
it("createProgress can create new questions mode progress file from existing set", async () => {
const createProgress = test.wrap(cloudFunctions.createProgress);
const setDataOne = {
"owner": userOne,
"public": false,
"title": setOne,
};
const vocabDataOne = {
"term": termOne,
"definition": definitionOne,
"sound": soundOne,
};
const vocabDataTwo = {
"term": termTwo,
"definition": definitionTwo,
"sound": soundTwo,
};
await firestore.collection("sets").doc(setOne).set(setDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabOne).set(vocabDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabTwo).set(vocabDataTwo);
const requestData = {
switch_language: false,
sets: [setOne],
mode: "questions",
limit: 2,
};
const progressId = await createProgress(requestData);
const progressDocId = firestore.collection("progress").doc(progressId);
const snapAfter = await progressDocId.get().then((doc) => doc.data());
const termOneSnapAfter = await progressDocId.collection("terms").doc(progressVocabOne).get().then((doc) => doc.data());
const definitionOneSnapAfter = await progressDocId.collection("definitions").doc(progressVocabOne).get().then((doc) => doc.data());
hamjest.assertThat(snapAfter.questions, hamjest.anyOf(
hamjest.is([progressVocabOne, progressVocabTwo]),
hamjest.is([progressVocabTwo, progressVocabOne])
));
assert.deepStrictEqual(snapAfter.correct, []);
assert.deepStrictEqual(snapAfter.incorrect, []);
assert.deepStrictEqual(snapAfter.current_correct, []);
assert.strictEqual(snapAfter.duration, null);
assert.strictEqual(snapAfter.progress, 0);
assert.deepStrictEqual(snapAfter.setIds, [setOne]);
assert.strictEqual(snapAfter.set_title, setOne);
assert.notStrictEqual(snapAfter.start_time, null);
assert.strictEqual(snapAfter.switch_language, false);
assert.strictEqual(snapAfter.uid, userOne);
assert.strictEqual(snapAfter.mode, "questions");
assert.strictEqual(snapAfter.typo, false);
assert.deepStrictEqual(termOneSnapAfter, {
item: termOne,
sound: soundOne,
});
assert.deepStrictEqual(definitionOneSnapAfter, {
item: definitionOne,
sound: soundOne,
});
});
it("createProgress can create new questions mode progress file from multiple existing sets", async () => {
const createProgress = test.wrap(cloudFunctions.createProgress);
const setDataOne = {
"owner": userOne,
"public": false,
"title": setOne,
};
const vocabDataOne = {
"term": termOne,
"definition": definitionOne,
"sound": soundOne,
};
const vocabDataTwo = {
"term": termTwo,
"definition": definitionTwo,
"sound": soundTwo,
};
const setDataTwo = {
"owner": userOne,
"public": false,
"title": setTwo,
};
const vocabDataThree = {
"term": termOne,
"definition": definitionOne,
"sound": soundOne,
};
const vocabDataFour = {
"term": termTwo,
"definition": definitionTwo,
"sound": soundTwo,
};
await firestore.collection("sets").doc(setOne).set(setDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabOne).set(vocabDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabTwo).set(vocabDataTwo);
await firestore.collection("sets").doc(setTwo).set(setDataTwo);
await firestore.collection("sets").doc(setTwo)
.collection("vocab").doc(vocabOne).delete();
await firestore.collection("sets").doc(setTwo)
.collection("vocab").doc(vocabTwo).delete();
await firestore.collection("sets").doc(setTwo)
.collection("vocab").doc(vocabThree).set(vocabDataThree);
await firestore.collection("sets").doc(setTwo)
.collection("vocab").doc(vocabFour).set(vocabDataFour);
const requestData = {
switch_language: false,
sets: [setOne, setTwo],
mode: "questions",
limit: 4,
};
const progressId = await createProgress(requestData);
const progressDocId = firestore.collection("progress").doc(progressId);
const snapAfter = await progressDocId.get().then((doc) => doc.data());
assert.deepStrictEqual(snapAfter.questions.sort(), [progressVocabOne, progressVocabTwo, progressVocabThree, progressVocabFour])
assert.deepStrictEqual(snapAfter.correct, []);
assert.deepStrictEqual(snapAfter.incorrect, []);
assert.deepStrictEqual(snapAfter.current_correct, []);
assert.strictEqual(snapAfter.duration, null);
assert.strictEqual(snapAfter.progress, 0);
assert.deepStrictEqual(snapAfter.setIds, [setOne, setTwo]);
assert.strictEqual(snapAfter.set_title, `${setOne} & ${setTwo}`);
assert.notStrictEqual(snapAfter.start_time, null);
assert.strictEqual(snapAfter.switch_language, false);
assert.strictEqual(snapAfter.uid, userOne);
assert.strictEqual(snapAfter.mode, "questions");
assert.strictEqual(snapAfter.typo, false);
});
it("createProgress can create new lives mode progress file from existing set", async () => {
const createProgress = test.wrap(cloudFunctions.createProgress);
const setDataOne = {
"owner": userOne,
"public": false,
"title": setOne,
};
const vocabDataOne = {
"term": termOne,
"definition": definitionOne,
"sound": soundOne,
};
const vocabDataTwo = {
"term": termTwo,
"definition": definitionTwo,
"sound": soundTwo,
};
await firestore.collection("sets").doc(setOne).set(setDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabOne).set(vocabDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabTwo).set(vocabDataTwo);
const requestData = {
switch_language: false,
sets: [setOne],
mode: "lives",
limit: 2,
};
const progressId = await createProgress(requestData);
const progressDocId = firestore.collection("progress").doc(progressId);
const snapAfter = await progressDocId.get().then((doc) => doc.data());
hamjest.assertThat(snapAfter.questions, hamjest.anyOf(
hamjest.is([progressVocabOne, progressVocabTwo]),
hamjest.is([progressVocabTwo, progressVocabOne])
));
assert.deepStrictEqual(snapAfter.correct, []);
assert.deepStrictEqual(snapAfter.incorrect, []);
assert.deepStrictEqual(snapAfter.current_correct, []);
assert.strictEqual(snapAfter.duration, null);
assert.strictEqual(snapAfter.progress, 0);
assert.deepStrictEqual(snapAfter.setIds, [setOne]);
assert.strictEqual(snapAfter.set_title, setOne);
assert.notStrictEqual(snapAfter.start_time, null);
assert.strictEqual(snapAfter.switch_language, false);
assert.strictEqual(snapAfter.uid, userOne);
assert.strictEqual(snapAfter.mode, "lives");
assert.strictEqual(snapAfter.lives, 2);
assert.strictEqual(snapAfter.typo, false);
});
it("createProgress can create new progress file from public set they aren't the owner of", async () => {
const createProgress = test.wrap(cloudFunctions.createProgress);
const setDataTwo = {
"owner": userTwo,
"public": true,
"title": setTwo,
};
const vocabDataOne = {
"term": termOne,
"definition": definitionOne,
"sound": soundOne,
};
const vocabDataTwo = {
"term": termTwo,
"definition": definitionTwo,
"sound": soundTwo,
};
await firestore.collection("sets").doc(setTwo).set(setDataTwo);
await firestore.collection("sets").doc(setTwo)
.collection("vocab").doc(vocabOne).set(vocabDataOne);
await firestore.collection("sets").doc(setTwo)
.collection("vocab").doc(vocabTwo).set(vocabDataTwo);
const requestData = {
switch_language: false,
sets: [setOne],
mode: "questions",
limit: 2,
};
firebase.assertSucceeds(createProgress(requestData));
});
it("createProgress can't create new progress file from non-public set they aren't the owner of", async () => {
const createProgress = test.wrap(cloudFunctions.createProgress);
const setDataTwo = {
"owner": userTwo,
"public": false,
"title": setTwo,
};
const vocabDataOne = {
"term": termOne,
"definition": definitionOne,
"sound": soundOne,
};
const vocabDataTwo = {
"term": termTwo,
"definition": definitionTwo,
"sound": soundTwo,
};
await firestore.collection("sets").doc(setTwo).set(setDataTwo);
await firestore.collection("sets").doc(setTwo)
.collection("vocab").doc(vocabOne).set(vocabDataOne);
await firestore.collection("sets").doc(setTwo)
.collection("vocab").doc(vocabTwo).set(vocabDataTwo);
const requestData = {
switch_language: false,
sets: [setTwo],
mode: "questions",
limit: 2,
};
firebase.assertFails(createProgress(requestData));
});
it("processAnswer updates progress documents appropriately when correct and incorrect answers provided", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne,
progressVocabTwo
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const termDataTwo = {
"item": termTwo,
"sound": soundTwo,
};
const definitionDataOne = {
"item": definitionOne,
"sound": soundOne,
};
const definitionDataTwo = {
"item": definitionTwo,
"sound": soundTwo,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("terms").doc(progressVocabTwo)
.set(termDataTwo);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
await progressDocId.collection("definitions").doc(progressVocabTwo)
.set(definitionDataTwo);
const firstTermAnswerRequestData = {
progressId: progressId,
answer: definitionOne,
};
const secondTermAnswerRequestData = {
progressId: progressId,
answer: definitionTwo,
};
const incorrectAnswerRequestData = {
progressId: progressId,
answer: incorrectAnswer,
};
const firstReturn = await processAnswer(incorrectAnswerRequestData);
hamjest.assertThat(firstReturn, hamjest.anyOf(
hamjest.is({
mode: "questions",
correct: false,
correctAnswers: [definitionOne],
currentVocabId: progressVocabOne,
moreAnswers: false,
nextPrompt: {
item: termOne,
sound: soundOne,
set_owner: userOne,
},
progress: 1,
totalQuestions: 3,
totalCorrect: 0,
totalIncorrect: 1,
typo: false,
}),
hamjest.is({
mode: "questions",
correct: false,
correctAnswers: [definitionOne],
currentVocabId: progressVocabOne,
moreAnswers: false,
nextPrompt: {
item: termTwo,
sound: soundTwo,
set_owner: userOne,
},
progress: 1,
totalQuestions: 3,
totalCorrect: 0,
totalIncorrect: 1,
typo: false,
})
));
const snapAfterIncorrectData = await progressDocId.get().then((doc) => doc.data());
hamjest.assertThat(snapAfterIncorrectData, hamjest.anyOf(
hamjest.is({
correct: [],
current_correct: [],
duration: null,
incorrect: [progressVocabOne],
progress: 1,
questions: [
progressVocabOne,
progressVocabOne,
progressVocabTwo
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
}),
hamjest.is({
correct: [],
current_correct: [],
duration: null,
incorrect: [progressVocabOne],
progress: 1,
questions: [
progressVocabOne,
progressVocabTwo,
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
})
));
if (firstReturn.nextPrompt.item === termOne) {
await processAnswer(firstTermAnswerRequestData);
await processAnswer(secondTermAnswerRequestData);
} else {
await processAnswer(secondTermAnswerRequestData);
await processAnswer(firstTermAnswerRequestData);
}
const snapAfterCorrectData = await progressDocId.get().then((doc) => doc.data());
hamjest.assertThat(snapAfterCorrectData.correct, hamjest.anyOf(
hamjest.is([progressVocabOne, progressVocabTwo]),
hamjest.is([progressVocabTwo, progressVocabOne])
));
hamjest.assertThat(snapAfterCorrectData.questions, hamjest.anyOf(
hamjest.is([progressVocabOne, progressVocabOne, progressVocabTwo]),
hamjest.is([progressVocabOne, progressVocabTwo, progressVocabOne])
));
assert.deepStrictEqual(snapAfterCorrectData.incorrect, [progressVocabOne]);
assert.deepStrictEqual(snapAfterCorrectData.current_correct, []);
assert.notStrictEqual(snapAfterCorrectData.duration, null);
assert.strictEqual(snapAfterCorrectData.progress, 3);
assert.strictEqual(snapAfterCorrectData.set_title, setOne);
assert.strictEqual(snapAfterCorrectData.start_time, 1627308670962);
assert.strictEqual(snapAfterCorrectData.switch_language, false);
assert.strictEqual(snapAfterCorrectData.uid, userOne);
assert.strictEqual(snapAfterCorrectData.mode, "questions");
assert.strictEqual(snapAfterCorrectData.typo, false);
});
it("processAnswer returns correct data", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": definitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const correctAnswerRequestData = {
progressId: progressId,
answer: definitionOne,
};
const incorrectAnswerRequestData = {
progressId: progressId,
answer: incorrectAnswer,
};
const returnAfterIncorrect = await processAnswer(incorrectAnswerRequestData);
const returnAfterCorrect = await processAnswer(correctAnswerRequestData);
assert.deepStrictEqual(returnAfterIncorrect, {
mode: "questions",
correct: false,
correctAnswers: [definitionOne],
currentVocabId: progressVocabOne,
moreAnswers: false,
nextPrompt: {
item: termOne,
sound: soundOne,
set_owner: userOne,
},
progress: 1,
totalQuestions: 2,
totalCorrect: 0,
totalIncorrect: 1,
typo: false,
});
assert.strictEqual(returnAfterCorrect.mode, "questions");
assert.strictEqual(returnAfterCorrect.correct, true)
assert.deepStrictEqual(returnAfterCorrect.correctAnswers, [definitionOne]);
assert.strictEqual(returnAfterCorrect.currentVocabId, progressVocabOne);
assert.notStrictEqual(returnAfterCorrect.duration, null);
assert.deepStrictEqual(returnAfterCorrect.incorrectAnswers, [progressVocabOne]);
assert.strictEqual(returnAfterCorrect.moreAnswers, false);
assert.strictEqual(returnAfterCorrect.nextPrompt, null);
assert.strictEqual(returnAfterCorrect.progress, 2);
assert.strictEqual(returnAfterCorrect.totalQuestions, 2);
assert.strictEqual(returnAfterCorrect.totalCorrect, 1);
assert.strictEqual(returnAfterCorrect.totalIncorrect, 1);
assert.strictEqual(returnAfterCorrect.mode, "questions");
});
it("processAnswer correctly handles correct and incorrect inputted answers when a vocab term has multiple required answers", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
const progressData = {
correct: [],
duration: null,
incorrect: [],
current_correct: [],
progress: 0,
questions: [
progressVocabOne,
progressVocabTwo
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const termDataTwo = {
"item": termTwo,
"sound": soundTwo,
};
const definitionDataOne = {
"item": doubleDefinitionOne,
"sound": soundOne,
};
const definitionDataTwo = {
"item": doubleDefinitionTwo,
"sound": soundTwo,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("terms").doc(progressVocabTwo)
.set(termDataTwo);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
await progressDocId.collection("definitions").doc(progressVocabTwo)
.set(definitionDataTwo);
const firstTermAnswerOneRequestData = {
progressId: progressId,
answer: "definition",
};
const firstTermAnswerTwoRequestData = {
progressId: progressId,
answer: "01",
};
const secondTermAnswerOneRequestData = {
progressId: progressId,
answer: "definition",
};
const secondTermAnswerTwoRequestData = {
progressId: progressId,
answer: "02",
};
const incorrectAnswerRequestData = {
progressId: progressId,
answer: incorrectAnswer,
};
const returnAfterCorrect = await processAnswer(firstTermAnswerOneRequestData);
assert.deepStrictEqual(returnAfterCorrect, {
mode: "questions",
correct: true,
correctAnswers: ["definition"],
currentVocabId: progressVocabOne,
moreAnswers: true,
nextPrompt: null,
progress: 0,
totalQuestions: 2,
totalCorrect: 0,
totalIncorrect: 0,
typo: false,
});
const snapAfterTermOneAnswerOneData = await progressDocId.get().then((doc) => doc.data());
assert.deepStrictEqual(snapAfterTermOneAnswerOneData, {
correct: [],
current_correct: ["definition"],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne,
progressVocabTwo
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
});
const returnAfterIncorrect = await processAnswer(incorrectAnswerRequestData);
const snapAfterIncorrectData = await progressDocId.get().then((doc) => doc.data());
hamjest.assertThat(snapAfterIncorrectData, hamjest.anyOf(
hamjest.is({
correct: [],
current_correct: [],
duration: null,
incorrect: [progressVocabOne],
progress: 1,
questions: [
progressVocabOne,
progressVocabOne,
progressVocabTwo
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
}),
hamjest.is({
correct: [],
current_correct: [],
duration: null,
incorrect: [progressVocabOne],
progress: 1,
questions: [
progressVocabOne,
progressVocabTwo,
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
})
));
if (returnAfterIncorrect.nextPrompt.item === termOne) {
await processAnswer(firstTermAnswerOneRequestData);
await processAnswer(firstTermAnswerTwoRequestData);
await processAnswer(secondTermAnswerOneRequestData);
await processAnswer(secondTermAnswerTwoRequestData);
} else {
await processAnswer(secondTermAnswerOneRequestData);
await processAnswer(secondTermAnswerTwoRequestData);
await processAnswer(firstTermAnswerOneRequestData);
await processAnswer(firstTermAnswerTwoRequestData);
}
const snapAfterCorrectData = await progressDocId.get().then((doc) => doc.data());
hamjest.assertThat(snapAfterCorrectData.correct, hamjest.anyOf(
hamjest.is([progressVocabOne, progressVocabTwo]),
hamjest.is([progressVocabTwo, progressVocabOne])
));
hamjest.assertThat(snapAfterCorrectData.questions, hamjest.anyOf(
hamjest.is([progressVocabOne, progressVocabOne, progressVocabTwo]),
hamjest.is([progressVocabOne, progressVocabTwo, progressVocabOne])
));
assert.deepStrictEqual(snapAfterCorrectData.incorrect, [progressVocabOne]);
assert.deepStrictEqual(snapAfterCorrectData.current_correct, []);
assert.notStrictEqual(snapAfterCorrectData.duration, null);
assert.strictEqual(snapAfterCorrectData.progress, 3);
assert.strictEqual(snapAfterCorrectData.set_title, setOne);
assert.strictEqual(snapAfterCorrectData.start_time, 1627308670962);
assert.strictEqual(snapAfterCorrectData.switch_language, false);
assert.strictEqual(snapAfterCorrectData.uid, userOne);
assert.strictEqual(snapAfterCorrectData.mode, "questions");
assert.strictEqual(snapAfterCorrectData.typo, false);
});
it("processAnswer ignores punctuation", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne,
progressVocabTwo
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const termDataTwo = {
"item": termTwo,
"sound": soundTwo,
};
const definitionDataOne = {
"item": punctuationDefinitionOne,
"sound": soundOne,
};
const definitionDataTwo = {
"item": definitionTwo,
"sound": soundTwo,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("terms").doc(progressVocabTwo)
.set(termDataTwo);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
await progressDocId.collection("definitions").doc(progressVocabTwo)
.set(definitionDataTwo);
const requestData = {
progressId: progressId,
answer: "definition\"'_-)(,.0 1",
};
const returnedData = await processAnswer(requestData);
assert.equal(returnedData.correct, true);
});
it("processAnswer detects typo correctly for slightly wrong short answer - Levenshtein distance 1", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": shortDefinitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const typoOneAnswerRequestData = {
progressId: progressId,
answer: shortDefinitionOneTypoOne,
};
const returnedData = await processAnswer(typoOneAnswerRequestData);
assert.deepStrictEqual(returnedData, {
typo: true,
});
});
it("processAnswer doesn't detect typo for short answer with too many mistakes - Levenshtein distance 2", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": shortDefinitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const typoTwoAnswerRequestData = {
progressId: progressId,
answer: shortDefinitionOneTypoTwo,
};
const returnedData = await processAnswer(typoTwoAnswerRequestData);
assert.equal(returnedData.typo, false);
});
it("processAnswer detects typo correctly for slightly wrong long answer - Levenshtein distance 1", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": definitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const typoOneAnswerRequestData = {
progressId: progressId,
answer: definitionOneTypoOne,
};
const returnedData = await processAnswer(typoOneAnswerRequestData);
assert.deepStrictEqual(returnedData, {
typo: true,
});
});
it("processAnswer detects typo correctly for slightly wrong long answer - Levenshtein distance 2", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": definitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const typoTwoAnswerRequestData = {
progressId: progressId,
answer: definitionOneTypoTwo,
};
const returnedData = await processAnswer(typoTwoAnswerRequestData);
assert.deepStrictEqual(returnedData, {
typo: true,
});
});
it("processAnswer detects typo correctly for slightly wrong long answer - Levenshtein distance 3", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": definitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const typoThreeAnswerRequestData = {
progressId: progressId,
answer: definitionOneTypoThree,
};
const returnedData = await processAnswer(typoThreeAnswerRequestData);
assert.deepStrictEqual(returnedData, {
typo: true,
});
});
it("processAnswer doesn't detect typo for long answer with too many mistakes - Levenshtein distance 4", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": definitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const typoFourAnswerRequestData = {
progressId: progressId,
answer: definitionOneTypoFour,
};
const returnedData = await processAnswer(typoFourAnswerRequestData);
assert.equal(returnedData.typo, false);
});
it("processAnswer detects typo correctly for empty answer", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": definitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const emptyAnswerRequestData = {
progressId: progressId,
answer: "",
};
const returnedData = await processAnswer(emptyAnswerRequestData);
assert.deepStrictEqual(returnedData, {
typo: true,
});
});
it("processAnswer stores correct typo status in progress db collection when typo detected", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": definitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const typoOneAnswerRequestData = {
progressId: progressId,
answer: definitionOneTypoOne,
};
await processAnswer(typoOneAnswerRequestData);
const snapAfter = await progressDocId.get().then((doc) => doc.data());
assert.strictEqual(snapAfter.typo, true);
});
it("processAnswer marks an answer as wrong on the second typo (typo - Levenshtein distance 1)", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: true,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": definitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const typoOneAnswerRequestData = {
progressId: progressId,
answer: definitionOneTypoOne,
};
const returnedData = await processAnswer(typoOneAnswerRequestData);
assert.equal(returnedData.correct, false);
const snapAfter = await progressDocId.get().then((doc) => doc.data());
assert.strictEqual(snapAfter.typo, false);
});
it("processAnswer marks an answer as wrong on the second typo (empty answer)", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: true,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": definitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const emptyAnswerRequestData = {
progressId: progressId,
answer: "",
};
const returnedData = await processAnswer(emptyAnswerRequestData);
assert.equal(returnedData.correct, false);
const snapAfter = await progressDocId.get().then((doc) => doc.data());
assert.strictEqual(snapAfter.typo, false);
});
it("processAnswer stores correct data in completed_progress db collection on test completion (set combination never tested before)", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
await firestore.collection("completed_progress")
.doc(`${setOne}__${setTwo}`)
.delete();
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: `${setOne}__${setTwo}`,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne, setTwo],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": definitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
const completedProgressDocId = firestore.collection("completed_progress").doc(`${setOne}__${setTwo}`);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const requestData = {
progressId: progressId,
answer: definitionOne,
};
await processAnswer(requestData);
const completedProgressSnapAfter = await completedProgressDocId.get().then((doc) => doc.data());
assert.deepStrictEqual(completedProgressSnapAfter, {
attempts: 1,
total_percentage: 100,
set_title: `${setOne} & ${setTwo}`,
});
});
it("processAnswer stores correct data in completed_progress db collection on test completion (set combination has been tested previously)", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
await firestore.collection("completed_progress")
.doc(`${setOne}__${setTwo}`)
.delete();
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: `${setOne}__${setTwo}`,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne, setTwo],
};
const completedProgressData = {
attempts: 1,
total_percentage: 0,
set_title: `${setOne} & ${setTwo}`,
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": definitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
const completedProgressDocId = firestore.collection("completed_progress").doc(`${setOne}__${setTwo}`);
await progressDocId.set(progressData);
await completedProgressDocId.set(completedProgressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const requestData = {
progressId: progressId,
answer: definitionOne,
};
await processAnswer(requestData);
const completedProgressSnapAfter = await completedProgressDocId.get().then((doc) => doc.data());
assert.deepStrictEqual(completedProgressSnapAfter, {
attempts: 2,
total_percentage: 100,
set_title: `${setOne} & ${setTwo}`,
});
});
it("processAnswer stores correct data in incorrect_answers db collection on incorrect answer (without typo and when not a member of any groups)", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
await deleteCollection(
firestore,
`/users/${userOne}/groups`,
500
);
await deleteCollection(
firestore,
`/incorrect_answers`,
500
);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": definitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const incorrectAnswerRequestData = {
progressId: progressId,
answer: incorrectAnswer,
};
await processAnswer(incorrectAnswerRequestData);
const snapAfter = await firestore.collection("incorrect_answers").get().then((querySnapshot) => querySnapshot.docs[0].data());
assert.deepStrictEqual(snapAfter, {
groups: [],
term: termOne,
definition: definitionOne,
uid: userOne,
switch_language: false,
answer: incorrectAnswer,
setIds: [setOne],
});
});
it("processAnswer stores correct data in incorrect_answers db collection on incorrect answer (without typo and when a member of groups)", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
await deleteCollection(
firestore,
`/users/${userOne}/groups`,
500
);
await deleteCollection(
firestore,
`/incorrect_answers`,
500
);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
};
const groupOneData = {
role: "owner",
};
const groupTwoData = {
role: "contributor",
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": definitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
const userGroupsCollectionId = firestore.collection("users").doc(userOne).collection("groups");
await progressDocId.set(progressData);
await userGroupsCollectionId.doc(groupOne).set(groupOneData);
await userGroupsCollectionId.doc(groupTwo).set(groupTwoData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const incorrectAnswerRequestData = {
progressId: progressId,
answer: incorrectAnswer,
};
await processAnswer(incorrectAnswerRequestData);
const snapAfter = await firestore.collection("incorrect_answers").get().then((querySnapshot) => querySnapshot.docs[0].data());
assert.deepStrictEqual(snapAfter, {
groups: [groupOne, groupTwo],
term: termOne,
definition: definitionOne,
uid: userOne,
switch_language: false,
answer: incorrectAnswer,
setIds: [setOne],
});
});
it("processAnswer stores no additional data in incorrect_answers db collection when typo detected", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
await deleteCollection(
firestore,
`/incorrect_answers`,
500
);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": definitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const typoAnswerRequestData = {
progressId: progressId,
answer: definitionOneTypoOne,
};
await processAnswer(typoAnswerRequestData);
const snapAfter = await firestore.collection("incorrect_answers").get().then((querySnapshot) => querySnapshot.docs);
assert.deepStrictEqual(snapAfter, []);
});
it("processAnswer stores no additional data in incorrect_answers db collection when correct answer provided", async () => {
const processAnswer = test.wrap(cloudFunctions.processAnswer);
await deleteCollection(
firestore,
`/incorrect_answers`,
500
);
const progressData = {
correct: [],
current_correct: [],
duration: null,
incorrect: [],
progress: 0,
questions: [
progressVocabOne
],
set_title: setOne,
start_time: 1627308670962,
switch_language: false,
uid: userOne,
mode: "questions",
typo: false,
setIds: [setOne],
};
const termDataOne = {
"item": termOne,
"sound": soundOne,
};
const definitionDataOne = {
"item": definitionOne,
"sound": soundOne,
};
const progressId = "progress_01";
const progressDocId = firestore.collection("progress").doc(progressId);
await progressDocId.set(progressData);
await progressDocId.collection("terms").doc(progressVocabOne)
.set(termDataOne);
await progressDocId.collection("definitions").doc(progressVocabOne)
.set(definitionDataOne);
const requestData = {
progressId: progressId,
answer: definitionOne,
};
await processAnswer(requestData);
const snapAfter = await firestore.collection("incorrect_answers").get().then((querySnapshot) => querySnapshot.docs);
assert.deepStrictEqual(snapAfter, []);
});
it("setAdmin can change other users' admin states", async () => {
/** NOTE
* Admin uid is M3JPrFRH6Fdo8XMUbF0l2zVZUCH3.
* This uid should be set in the function's code during testing.
*/
const setAdmin = test.wrap(cloudFunctions.setAdmin);
const targetId = await admin.auth().createUser({
email: "user_01@mgrove.uk",
password: "user1234",
}).then((user) => user.uid);
firebase.assertSucceeds(await setAdmin({
targetUser: targetId,
adminState: true,
}));
await admin.auth().deleteUser(targetId);
});
it("setAdmin can't change current user's admin state", () => {
/** NOTE
* Admin uid is M3JPrFRH6Fdo8XMUbF0l2zVZUCH3.
* This uid should be set in the function's code during testing.
*/
const setAdmin = test.wrap(cloudFunctions.setAdmin);
const targetId = "M3JPrFRH6Fdo8XMUbF0l2zVZUCH3";
firebase.assertFails(setAdmin({
targetUser: targetId,
adminState: false,
}));
});
it("addSetToGroup can add existing set to existing group", async () => {
const addSetToGroup = test.wrap(cloudFunctions.addSetToGroup);
const setDataOne = {
"owner": userOne,
"public": true,
"title": setOne,
};
const vocabDataOne = {
"term": termOne,
"definition": definitionOne,
"sound": soundOne,
};
const vocabDataTwo = {
"term": termTwo,
"definition": definitionTwo,
"sound": soundTwo,
};
let groupUsers = {};
groupUsers[userOne] = "owner";
const groupDataOne = {
"display_name": groupOne,
"join_code": "abcd1234",
"sets": [],
"users": groupUsers,
};
const userGroupDataOne = {
role: "owner",
};
await firestore.collection("sets").doc(setOne).set(setDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabOne).set(vocabDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabTwo).set(vocabDataTwo);
await firestore.collection("groups").doc(groupOne).set(groupDataOne);
await firestore.collection("users").doc(userOne)
.collection("groups").doc(groupOne).set(userGroupDataOne);
firebase.assertSucceeds(addSetToGroup({
groupId: groupOne,
setId: setOne,
}));
});
it("addSetToGroup can't add existing set that's not public and isn't theirs to existing group", async () => {
const addSetToGroup = test.wrap(cloudFunctions.addSetToGroup);
const setDataOne = {
"owner": userTwo,
"public": false,
"title": setOne,
};
const vocabDataOne = {
"term": termOne,
"definition": definitionOne,
"sound": soundOne,
};
const vocabDataTwo = {
"term": termTwo,
"definition": definitionTwo,
"sound": soundTwo,
};
let groupUsers = {};
groupUsers[userOne] = "owner";
const groupDataOne = {
"display_name": groupOne,
"join_code": "abcd1234",
"sets": [],
"users": groupUsers,
};
const userGroupDataOne = {
role: "owner",
};
await firestore.collection("sets").doc(setOne).set(setDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabOne).set(vocabDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabTwo).set(vocabDataTwo);
await firestore.collection("groups").doc(groupOne).set(groupDataOne);
await firestore.collection("users").doc(userOne)
.collection("groups").doc(groupOne).set(userGroupDataOne);
firebase.assertFails(addSetToGroup({
groupId: groupOne,
setId: setOne,
}));
});
it("addSetToGroup can't add existing set to existing group when their role is member and they aren't admin", async () => {
const addSetToGroup = test.wrap(cloudFunctions.addSetToGroup);
const setDataOne = {
"owner": userOne,
"public": true,
"title": setOne,
};
const vocabDataOne = {
"term": termOne,
"definition": definitionOne,
"sound": soundOne,
};
const vocabDataTwo = {
"term": termTwo,
"definition": definitionTwo,
"sound": soundTwo,
};
let groupUsers = {};
groupUsers[userOne] = "member";
const groupDataOne = {
"display_name": groupOne,
"join_code": "abcd1234",
"sets": [],
"users": groupUsers,
};
const userGroupDataOne = {
role: "member",
};
await firestore.collection("sets").doc(setOne).set(setDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabOne).set(vocabDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabTwo).set(vocabDataTwo);
await firestore.collection("groups").doc(groupOne).set(groupDataOne);
await firestore.collection("users").doc(userOne)
.collection("groups").doc(groupOne).set(userGroupDataOne);
firebase.assertFails(addSetToGroup({
groupId: groupOne,
setId: setOne,
}));
});
it("removeSetFromGroup can remove existing set from existing group it is already a part of when group owner", async () => {
const removeSetFromGroup = test.wrap(cloudFunctions.removeSetFromGroup);
const setDataOne = {
owner: userOne,
public: true,
title: setOne,
groups: [groupOne],
};
const vocabDataOne = {
term: termOne,
definition: definitionOne,
sound: soundOne,
};
const vocabDataTwo = {
term: termTwo,
definition: definitionTwo,
sound: soundTwo,
};
let groupUsers = {};
groupUsers[userOne] = "owner";
const groupDataOne = {
display_name: groupOne,
join_code: "abcd1234",
sets: [setOne],
users: groupUsers,
};
const userGroupDataOne = {
role: "owner",
};
await firestore.collection("sets").doc(setOne).set(setDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabOne).set(vocabDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabTwo).set(vocabDataTwo);
await firestore.collection("groups").doc(groupOne).set(groupDataOne);
await firestore.collection("users").doc(userOne)
.collection("groups").doc(groupOne).set(userGroupDataOne);
firebase.assertSucceeds(removeSetFromGroup({
groupId: groupOne,
setId: setOne,
}));
});
it("removeSetFromGroup can't remove existing set from existing group it is already a part of when not group owner", async () => {
const removeSetFromGroup = test.wrap(cloudFunctions.removeSetFromGroup);
const setDataOne = {
owner: userOne,
public: true,
title: setOne,
groups: [groupOne],
};
const vocabDataOne = {
term: termOne,
definition: definitionOne,
sound: soundOne,
};
const vocabDataTwo = {
term: termTwo,
definition: definitionTwo,
sound: soundTwo,
};
let groupUsers = {};
groupUsers[userOne] = "collaborator";
const groupDataOne = {
display_name: groupOne,
join_code: "abcd1234",
sets: [setOne],
users: groupUsers,
};
const userGroupDataOne = {
role: "collaborator",
};
await firestore.collection("sets").doc(setOne).set(setDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabOne).set(vocabDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabTwo).set(vocabDataTwo);
await firestore.collection("groups").doc(groupOne).set(groupDataOne);
await firestore.collection("users").doc(userOne)
.collection("groups").doc(groupOne).set(userGroupDataOne);
firebase.assertFails(removeSetFromGroup({
groupId: groupOne,
setId: setOne,
}));
});
it("removeSetFromGroup can't remove existing set from existing group it is not already a part of when group owner", async () => {
const removeSetFromGroup = test.wrap(cloudFunctions.removeSetFromGroup);
const setDataOne = {
owner: userOne,
public: true,
title: setOne,
groups: [],
};
const vocabDataOne = {
term: termOne,
definition: definitionOne,
sound: soundOne,
};
const vocabDataTwo = {
term: termTwo,
definition: definitionTwo,
sound: soundTwo,
};
let groupUsers = {};
groupUsers[userOne] = "owner";
const groupDataOne = {
display_name: groupOne,
join_code: "abcd1234",
sets: [],
users: groupUsers,
};
const userGroupDataOne = {
role: "owner",
};
await firestore.collection("sets").doc(setOne).set(setDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabOne).set(vocabDataOne);
await firestore.collection("sets").doc(setOne)
.collection("vocab").doc(vocabTwo).set(vocabDataTwo);
await firestore.collection("groups").doc(groupOne).set(groupDataOne);
await firestore.collection("users").doc(userOne)
.collection("groups").doc(groupOne).set(userGroupDataOne);
firebase.assertFails(removeSetFromGroup({
groupId: groupOne,
setId: setOne,
}));
});
it("createGroup can create new group", async () => {
const createGroup = test.wrap(cloudFunctions.createGroup);
const groupId = await createGroup(groupOne);
const groupDocId = firestore.collection("groups").doc(groupId);
const snapGroupAfter = await groupDocId.get().then((doc) => doc.data());
const userGroupDocId = firestore.collection("users").doc(userOne).collection("groups").doc(groupId);
const joinCodeDocId = firestore.collection("join_codes").doc(snapGroupAfter.join_code);
const snapUserGroupAfter = await userGroupDocId.get();
const joinCodeSnap = await joinCodeDocId.get();
assert.strictEqual(snapGroupAfter.display_name, groupOne);
assert.deepStrictEqual(snapGroupAfter.sets, []);
assert.deepStrictEqual(snapGroupAfter.users, {});
assert.notStrictEqual(snapGroupAfter.join_code, null);
assert.deepStrictEqual(snapUserGroupAfter.data(), {
role: "owner"
});
assert.deepStrictEqual(joinCodeSnap.data(), {
group: groupId,
});
});
/*xit("getGroupMembers returns group members correctly", async () => {
const getGroupMembers = test.wrap(cloudFunctions.getGroupMembers);
const userGroupDataOne = {
role: "owner",
};
const userGroupDataTwo = {
role: "collaborator",
};
const userGroupDataThree = {
role: "member",
};
const userOneId = admin.auth().createUser({
email: "user_01@mgrove.uk",
password: "user1234",
displayName: "User 01",
}).then((user) => {
return user.uid;
});
const userTwoId = admin.auth().createUser({
email: "user_02@mgrove.uk",
password: "user1234",
displayName: "User 02",
}).then((user) => {
return user.uid;
});
const userThreeId = admin.auth().createUser({
email: "user_03@mgrove.uk",
password: "user1234",
displayName: "User 03",
}).then((user) => {
return user.uid;
});
await Promise.all([
userOneId,
userTwoId,
userThreeId
]);
const groupDataOne = {
display_name: groupOne,
join_code: "abcd1234",
sets: [],
users: {
[userOneId]: "owner",
[userTwoId]: "collaborator",
[userThreeId]: "member",
},
};
await Promise.all([
firestore.collection("groups").doc(groupOne).set(groupDataOne),
firestore.collection("users").doc(userOneId)
.collection("groups").doc(groupOne).set(userGroupDataOne),
firestore.collection("users").doc(userTwoId)
.collection("groups").doc(groupOne).set(userGroupDataTwo),
firestore.collection("users").doc(userThreeId)
.collection("groups").doc(groupOne).set(userGroupDataThree),
]);
const returnData = await getGroupMembers(groupOne);
await Promise.all([
admin.auth().deleteUser(userOneId),
admin.auth().deleteUser(userTwoId),
admin.auth().deleteUser(userThreeId),
])
assert.deepStrictEqual(returnData, {
owners: [
{
uid: userOneId,
displayName: "User 01",
}
],
collaborators: [
{
uid: userTwoId,
displayName: "User 02",
}
],
members: [
{
uid: userThreeId,
displayName: "User 03",
}
],
});
});*/
});