From 366d0708fcc4eb0e5bcd4042037c1d62d326f467 Mon Sep 17 00:00:00 2001 From: CaptainQuack <104166150+Captain-Quack@users.noreply.github.com> Date: Sun, 25 Jun 2023 22:05:38 -0500 Subject: [PATCH 01/24] Update package.json I think I did this right... *fingers crossed* --- package.json | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index c205c8b26..76b34b97f 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,15 @@ "version": "4.0.0", "type": "module", "scripts": { - "build": "npm run compile-react && npm run sass", + "test": "mocha", + "build": "npm run compile-react && npm run sass && npm test", "compile-react": "babel client/database/index.jsx -o client/database/index.js", "start": "node server/server.js", "sass": "sass scss/light.scss client/bootstrap/light.css && sass scss/dark.scss client/bootstrap/dark.css", "lint": "eslint .", - "test:frontend": "node tests/scorer.test.js", - "test:backend": "node tests/database.test.js" + "test:frontend": "mocha tests/scorer.test.js", + "test:backend": "mocha tests/database.test.js", + "test:all": "npm run test:frontend && npm run test:backend" }, "dependencies": { "bootstrap": "5.2.3", @@ -40,6 +42,8 @@ "@babel/preset-react": "^7.18.6", "eslint": "^8.28.0", "eslint-plugin-react": "^7.31.11", - "sass": "^1.56.2" + "sass": "^1.56.2", + "mocha": "^10.2.0", + "chai": "^4.3.7" } } From 3cf4940128c767f159ae164621cd9c8017b7d807 Mon Sep 17 00:00:00 2001 From: CaptainQuack <104166150+Captain-Quack@users.noreply.github.com> Date: Mon, 26 Jun 2023 12:12:56 -0500 Subject: [PATCH 02/24] Database test overhaul - part 1 --- tests/database.test.js | 71 +++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 19 deletions(-) diff --git a/tests/database.test.js b/tests/database.test.js index 6a56e11a9..10a1b664e 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -1,25 +1,58 @@ import { getQuery, getPacket, getSet, getRandomBonuses, getRandomTossups, getNumPackets, reportQuestion } from '../database/questions'; +const assert = require('chai').assert; -async function testTiming(count) { - const packetNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]; - - console.time('getQuery (empty string)'); - for (let i = 0; i < count; i++) { - await getQuery({ questionType: 'all', verbose: false }); - } - console.timeEnd('getQuery (empty string)'); - - console.time('getQuery (string = abc)'); - for (let i = 0; i < count; i++) { - await getQuery({ queryString: 'abc', questionType: 'all', verbose: false }); - } - console.timeEnd('getQuery (string = abc)'); +/* + Note: this.timeout(n) asserts that each `it` block individually takes less then "n" millisecconds. + It's inherited by the nested test suites, and can be overriden. +*/ - console.time('getQuery (string = cesaire), ignore diacritics'); - for (let i = 0; i < count; i++) { - await getQuery({ queryString: 'cesaire', questionType: 'all', verbose: false, ignoreDiacritics: true }); - } - console.timeEnd('getQuery (string = cesaire), ignore diacritics'); +describe("Performance Tests", function() { + const packetNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]; +/* +the "formula" for the timeing was done by replicating the request on the website, +and multiplying the execution time by 2 or 3 (usually.) +*/ + describe("getQuery", function() { + + it("empty string (under 2000ms)", async function() { + this.timeout(2000); + for (let i = 0; i < count; i++) { + await getQuery({ questionType: 'all', verbose: false}); + } + }); + it("'abc' (under 3000ms)", async function() { + this.timeout(3000); + for (let i = 0; i < count; i++) { + await getQuery({ queryString: 'abc', questionType: 'all', verbose: false }); + } + }); + it("'abc', max length 401 (under 5000ms)", async function() { + this.timeout(5000); + for (let i = 0; i < count; i++) { + await getQuery({ queryString: 'abc', questionType: 'all', verbose: false, maxReturnLength: 401 }); + } + }); + it("'([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', with regex (under 10000ms)", async function() { + this.timeout(10000); + for (let i = 0; i < count; i++) { + await getQuery({ queryString: 'abc', questionType: 'all', verbose: false }); + } + }); + it("'cesare', ignore diacritics (under 18000ms)", async function() { + this.timeout(18000); + for (let i = 0; i < count; i++) { + await getQuery({ queryString: 'cesaire', questionType: 'all', verbose: false, ignoreDiacritics: true }); + } + }); + + }); + describe("getPacket", function() { + it("2018 PACE NSC (under 2000", async function() { + + }); + + }); +}); console.time('getPacket'); for (let i = 0; i < count; i++) { From af32129833605522cfca9a43cdd0a07947ceb060 Mon Sep 17 00:00:00 2001 From: CaptainQuack <104166150+Captain-Quack@users.noreply.github.com> Date: Mon, 26 Jun 2023 14:54:50 -0500 Subject: [PATCH 03/24] Test timing done --- tests/database.test.js | 108 +++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 68 deletions(-) diff --git a/tests/database.test.js b/tests/database.test.js index 10a1b664e..21d4c9e87 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -6,88 +6,60 @@ const assert = require('chai').assert; It's inherited by the nested test suites, and can be overriden. */ -describe("Performance Tests", function() { +function testTiming(count) { +return describe(`Performance Tests with ${count} repetitions`, function() { const packetNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]; /* the "formula" for the timeing was done by replicating the request on the website, -and multiplying the execution time by 2 or 3 (usually.) +and multiplying the execution time by 2 or 3 (usually), and the "count" parameter */ + function testRequest(name, timeout, func, params = false) { + it(`${name} (under ${timeout * count}ms)`, async function() { + this.timeout(timeout * count); + for (let i = 0; i < count; i++) { + await func(params); + } + } + } describe("getQuery", function() { - - it("empty string (under 2000ms)", async function() { - this.timeout(2000); - for (let i = 0; i < count; i++) { - await getQuery({ questionType: 'all', verbose: false}); - } - }); - it("'abc' (under 3000ms)", async function() { - this.timeout(3000); - for (let i = 0; i < count; i++) { - await getQuery({ queryString: 'abc', questionType: 'all', verbose: false }); - } - }); - it("'abc', max length 401 (under 5000ms)", async function() { - this.timeout(5000); - for (let i = 0; i < count; i++) { - await getQuery({ queryString: 'abc', questionType: 'all', verbose: false, maxReturnLength: 401 }); - } - }); - it("'([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', with regex (under 10000ms)", async function() { - this.timeout(10000); - for (let i = 0; i < count; i++) { - await getQuery({ queryString: 'abc', questionType: 'all', verbose: false }); - } - }); - it("'cesare', ignore diacritics (under 18000ms)", async function() { - this.timeout(18000); + testRequest("empty string", 2000, getQuery, { questionType: 'all', verbose: false}); + testRequest("'abc'", 3000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false }); + testRequest("'abc', max length 401", 5000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false, maxReturnLength: 401 }); + testRequest("'([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', regex", 10000, getQuery, { queryString: '([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', questionType: 'all', verbose: false, regex: true }); + testRequest("'cesare', ignore diacritics", 170000, getQuery, { queryString: 'cesaire', questionType: 'all', verbose: false, ignoreDiacritics: true }); + }); + describe("getPacket", function() { + testRequest("2018 PACE NSC", 1000, getPacket, {setName: '2018 PACE NSC', packetNumber: 5 }); + }); + describe("getSet", function() { + testRequest('2018 PACE NSC', 1000, getSet, { setName: '2018 PACE NSC', packetNumbers, questionType: 'bonus' }); + testRequest('Invalid set name', 2500, getSet, { setName: '( ̄y▽ ̄)╭', packetNumbers, questionType: 'bonus' }); + }); + describe("Random Functions", function() { + this.timeout(2500) + testRequest("getRandomBonuses", 2500, getRandomBonuses); + testRequest("getRandomTossups", 2500, getRandomTossups); + }); + // The report function can't use tests requests because it requires more then one parameter :( + describe("reportQuestion", function() { + it("reportQuestion", async function() { for (let i = 0; i < count; i++) { - await getQuery({ queryString: 'cesaire', questionType: 'all', verbose: false, ignoreDiacritics: true }); + await reportQuestion('630020e3cab8fa6d1490b8ea', 'other', 'test'); } }); - - }); - describe("getPacket", function() { - it("2018 PACE NSC (under 2000", async function() { - - }); - }); + }); - - console.time('getPacket'); - for (let i = 0; i < count; i++) { - await getPacket({ setName: '2018 PACE NSC', packetNumber: 5 }); - } - console.timeEnd('getPacket'); - - console.time('getSet'); - for (let i = 0; i < count; i++) { - await getSet({ setName: '2018 PACE NSC', packetNumbers, questionType: 'bonus' }); - } - console.timeEnd('getSet'); - - console.time('getRandomBonuses'); - for (let i = 0; i < count; i++) { - await getRandomBonuses(); - } - console.timeEnd('getRandomBonuses'); - - console.time('getRandomTossups'); - for (let i = 0; i < count; i++) { - await getRandomTossups(); - } - console.timeEnd('getRandomTossups'); - - console.time('reportQuestion'); - for (let i = 0; i < count; i++) { - await reportQuestion('630020e3cab8fa6d1490b8ea', 'other', 'test'); - } - console.timeEnd('reportQuestion'); } -async function testCorrectness() { +async function testCorrectness() { + async function testQuery(tossups, bonuses) { + console.assert(tossups + + } + const { tossups, bonuses } = await getQuery({ queryString: 'qigong', setName: '2023 ACF Regionals', verbose: false, ignoreDiacritics: true }); console.assert(tossups && bonuses); console.assert(tossups.count === 1, `${tossups.count} != ${1}`); From e3747828c5b33c6551717d73aee7377c4d46e7e2 Mon Sep 17 00:00:00 2001 From: CaptainQuack <104166150+Captain-Quack@users.noreply.github.com> Date: Mon, 26 Jun 2023 17:49:17 -0500 Subject: [PATCH 04/24] Finish draft database testing --- tests/database.test.js | 138 +++++++++++++++++------------------------ 1 file changed, 56 insertions(+), 82 deletions(-) diff --git a/tests/database.test.js b/tests/database.test.js index 21d4c9e87..928713043 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -1,5 +1,6 @@ import { getQuery, getPacket, getSet, getRandomBonuses, getRandomTossups, getNumPackets, reportQuestion } from '../database/questions'; const assert = require('chai').assert; +const packetNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]; /* Note: this.timeout(n) asserts that each `it` block individually takes less then "n" millisecconds. @@ -8,7 +9,6 @@ const assert = require('chai').assert; function testTiming(count) { return describe(`Performance Tests with ${count} repetitions`, function() { - const packetNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]; /* the "formula" for the timeing was done by replicating the request on the website, and multiplying the execution time by 2 or 3 (usually), and the "count" parameter @@ -54,95 +54,69 @@ and multiplying the execution time by 2 or 3 (usually), and the "count" paramete async function testCorrectness() - { - async function testQuery(tossups, bonuses) { - console.assert(tossups - +{ + return describe("Correctness Tests", function() { + async function testQuery(testName, params, tossupCount, bonusCount, expectedFirstTossupQueston, expectedFirstTossupAnswer) { + it(testName, async function() { + const { tossups, bonuses } = await getQuery(params); + assert.isOk(tossups, "tossups"); + assert.isOk(bonuses, "bonuses"); + assert.propertyVal(tossups, 'count', tossupCount, "tossup count"); + assert.propertyVal(bonuses, 'count', bonusCount, "bonus count"); + assert.strictEqual(tossups.questionArray[0].question, expectedFirstTossupQueston, "tossup array - question"); + assert.strictEqual(tossups.questionArray[0].answer, expectedFirstTossupAnswer, "tossup array - answer"); + }); + } + { + const question = 'Note to moderator: Read the answerline carefully. A simplified, secular form of this practice is nicknamed “the 24.” Arthur Rosenfeld hosted a PBS program that instructed this practice for longevity and taught that chewing food 36 times can enhance the sensitivity, or “listening power,” outlined in this practice’s “classics.” The last Saturday in April is a worldwide holiday for this practice, whose methods of silk reeling and pushing hands may be attributed to its legendary inventor Zhāng Sānfēng (“jahng sahn-fung”) of the Wǔdāng (“oo-dahng”) Mountains. The Sūn (“swun”) and Yáng lineages are two of the five major styles of this type of nèijiā (“nay-jʼyah”), which originated in Chén (“chun”) Village. Unlike repetitive qìgōng (“chee-gong”), this balance-promoting practice’s “frames” link up to 108 specific postures. For 10 points, the elderly in Kowloon Park often perform what internal martial art whose routines feature slow movements?'; + const answer = 'tai chi [or tàijíquán or t’ai chi ch’üan; accept shadowboxing; prompt on Chinese martial arts until read; prompt on wǔshù or guóshù or kuoshu; prompt on exercise, physical activity, or meditation; prompt on nèijiā or nèigōng or nèijìng until “nèijiā” is read; prompt on qìgōng, ch‘i kung, chi gung, or chi ‘ung until “qìgōng” is read; prompt on Wǔdāng quán until read; prompt on traditional Chinese medicine or TCM or Zhōngyī; reject “boxing”]'; + testQuery("getQuery - 'qigong', 2023 ACF Regionals, ignore diacritics", + { queryString: 'qigong', setName: '2023 ACF Regionals', verbose: false, ignoreDiacritics: true }, 1, 0, question, answer); + } + { + const { tossups, bonuses } = await getQuery({ queryString: 'newton', questionType: 'all', setName: '2018 PACE NSC', verbose: false, maxReturnLength: 400 }); + const question = 'A theorem introduced by this man gives a formula to find the radii of four mutually tangent circles. The second book of a work by this mathematician consists of a classification of algebraic curves, including his namesake "folium." This man is the inventor, and sometimes the namesake, of the field of analytic geometry. This man\'s three (*) "laws of nature" were a major influence on Isaac Newton\'s laws of motion. An upper limit on the number of positive roots of a polynomial can be found using this mathematician\'s "rule of signs." In two dimensions, ordered pairs are used to represent the x- and y-coordinates of numbers in his namesake coordinate system. For 10 points, name this French mathematician, who, in a famous work of philosophy, stated "Cogito ergo sum."'; + const answer = 'René Descartes (day-CART)'; + testQuery("getQuery - 'newton', all questions, 2018 PACE NSC, return length = 400", + { queryString: 'newton', questionType: 'all', setName: '2018 PACE NSC', verbose: false, maxReturnLength: 400 }, + 5, 2, question, answer); + } + async function testPacketorSet(testName, params, tossupCount, bonusCount, expectedFirstTossupQueston, expectedFirstTossupAnswer, expectedFirstLeadin) { + it(testName, async function() { + const tossups = await getSet({ ...params, questionType: 'tossup' }); + const bonuses = await getSet({ ...params, questionType: 'bonus' }); + + assert.isOk(tossups, "tossups"); + assert.isOk(bonuses, "bonuses"); + assert.propertyVal(tossups, 'length', tossupCount, "tossup count"); + assert.propertyVal(bonuses, 'length', bonusCount, "bonus count"); + assert.propertyVal(bonuses[0], 'leadin', expectedFirstLeadin, "bonuses - leadins"); + assert.strictEqual(tossups[0].question, expectedFirstTossupQueston, "tossups - question"); + assert.strictEqual(tossups[0].answer, expectedFirstTossupAnswer, "tossups - answer"); + }); + } + { + const question = 'In his final appearance, this character experiences a severe toothache after asserting "as a weapon I may be of some use. But as a man, I\'m a wreck," then leaves to join King Milan\'s forces. This man buys a painting of two boys fishing, and commissions a portrait, from his fellow expatriate Mihailov. He is shocked to learn that his lover is pregnant between one scene in which he glimpses his rival Makhotin\'s chestnut (*) Gladiator, and another scene in which he rides his own horse Frou-Frou to death. This character first encounters his future lover at a railway station, where a worker is crushed by a train, and is initially interested in Kitty Shcherbatsky. For 10 points, name this Leo Tolstoy character, a nobleman who has an affair with Anna Karenina.'; + const answer = 'Count Alexei (Kirillovich) Vronsky [prompt on Alexei]'; + const leadin = 'The 170 men who rowed each of these ships often came from Piraeus and were thetes, the lowest class of citizen. For 10 points each:'; + testPacketorSet("getPacket -\n 2018 PACE NSC, Packet 5", { setName: '2018 PACE NSC', packetNumber: 5 } tossups, bonuses, 21, 21, question, answer, leadin); } - - const { tossups, bonuses } = await getQuery({ queryString: 'qigong', setName: '2023 ACF Regionals', verbose: false, ignoreDiacritics: true }); - console.assert(tossups && bonuses); - console.assert(tossups.count === 1, `${tossups.count} != ${1}`); - console.assert(bonuses.count === 0, `${bonuses.count} != ${0}`); - console.assert( - tossups.questionArray[0].question === 'Note to moderator: Read the answerline carefully. A simplified, secular form of this practice is nicknamed “the 24.” Arthur Rosenfeld hosted a PBS program that instructed this practice for longevity and taught that chewing food 36 times can enhance the sensitivity, or “listening power,” outlined in this practice’s “classics.” The last Saturday in April is a worldwide holiday for this practice, whose methods of silk reeling and pushing hands may be attributed to its legendary inventor Zhāng Sānfēng (“jahng sahn-fung”) of the Wǔdāng (“oo-dahng”) Mountains. The Sūn (“swun”) and Yáng lineages are two of the five major styles of this type of nèijiā (“nay-jʼyah”), which originated in Chén (“chun”) Village. Unlike repetitive qìgōng (“chee-gong”), this balance-promoting practice’s “frames” link up to 108 specific postures. For 10 points, the elderly in Kowloon Park often perform what internal martial art whose routines feature slow movements?', - tossups.questionArray[0].question - ); - console.assert( - tossups.questionArray[0].answer === 'tai chi [or tàijíquán or t’ai chi ch’üan; accept shadowboxing; prompt on Chinese martial arts until read; prompt on wǔshù or guóshù or kuoshu; prompt on exercise, physical activity, or meditation; prompt on nèijiā or nèigōng or nèijìng until “nèijiā” is read; prompt on qìgōng, ch‘i kung, chi gung, or chi ‘ung until “qìgōng” is read; prompt on Wǔdāng quán until read; prompt on traditional Chinese medicine or TCM or Zhōngyī; reject “boxing”]', - tossups.questionArray[0].answer - ); - } - - { - const { tossups, bonuses } = await getQuery({ queryString: 'newton', questionType: 'all', setName: '2018 PACE NSC', verbose: false, maxReturnLength: 400 }); - console.assert(tossups && bonuses); - console.assert(tossups.count === 5, `${tossups.count} != 5`); - console.assert(bonuses.count === 2, `${bonuses.count} != 2`); - console.assert( - tossups.questionArray[0].question === 'A theorem introduced by this man gives a formula to find the radii of four mutually tangent circles. The second book of a work by this mathematician consists of a classification of algebraic curves, including his namesake "folium." This man is the inventor, and sometimes the namesake, of the field of analytic geometry. This man\'s three (*) "laws of nature" were a major influence on Isaac Newton\'s laws of motion. An upper limit on the number of positive roots of a polynomial can be found using this mathematician\'s "rule of signs." In two dimensions, ordered pairs are used to represent the x- and y-coordinates of numbers in his namesake coordinate system. For 10 points, name this French mathematician, who, in a famous work of philosophy, stated "Cogito ergo sum."', - tossups.questionArray[0].question - ); - console.assert( - tossups.questionArray[0].answer === 'René Descartes (day-CART)', - tossups.questionArray[0].answer - ); - } - - { - const { tossups, bonuses } = await getPacket({ setName: '2018 PACE NSC', packetNumber: 5 }); - console.assert(tossups && bonuses); - console.assert(tossups.length === 21, `${tossups.length} != 21`); - console.assert(bonuses.length === 21, `${bonuses.length} != 21`); - console.assert( - tossups[0].question === 'In his final appearance, this character experiences a severe toothache after asserting "as a weapon I may be of some use. But as a man, I\'m a wreck," then leaves to join King Milan\'s forces. This man buys a painting of two boys fishing, and commissions a portrait, from his fellow expatriate Mihailov. He is shocked to learn that his lover is pregnant between one scene in which he glimpses his rival Makhotin\'s chestnut (*) Gladiator, and another scene in which he rides his own horse Frou-Frou to death. This character first encounters his future lover at a railway station, where a worker is crushed by a train, and is initially interested in Kitty Shcherbatsky. For 10 points, name this Leo Tolstoy character, a nobleman who has an affair with Anna Karenina.', - tossups[0].question - ); - console.assert( - tossups[0].answer === 'Count Alexei (Kirillovich) Vronsky [prompt on Alexei]', - tossups[0].answer - ); - console.assert( - bonuses[0].leadin === 'The 170 men who rowed each of these ships often came from Piraeus and were thetes, the lowest class of citizen. For 10 points each:', - bonuses[0].leadin - ); - } - - { - const tossups = await getSet({ setName: '2016 NASAT', packetNumbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], questionType: 'tossup' }); - const bonuses = await getSet({ setName: '2016 NASAT', packetNumbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], questionType: 'bonus' }); - - console.assert(tossups.length === 336, `${tossups.length} != 336`); - console.assert(bonuses.length === 336, `${bonuses.length} != 336`); - console.assert( - tossups[0].question === 'Besides his treatise on the Divine Names, the most notable work by Pseudo-Dionysius the Areopagite discusses these things. The phrase "Grigori" refers to some of these things that are heavily described in the apocryphal Books of Enoch. First Corinthians 11 argues that, specifically because of these things, women should wear head coverings when praying or prophesying. Tertullian suggested these things are what created the gigantic Nephilim. In the Talmud, Elisha ben Abuyah declares that there are "two powers in heaven" when he sees one of these things named Metatron. The book of Daniel mentions one of these beings by name, saying he will help fight the princes of Persia and protect Israel. For 10 points, name these celestial figures that include Gabriel and Michael.', - tossups[0].question - ); - console.assert( - tossups[0].answer === 'angels [or archangels; or fallen angels; or Watchers; or mal\'akhim; or Grigori until it is read]', - tossups[0].answer - ); - console.assert( - bonuses[0].leadin === 'In a painting by this artist, a heavily-garlanded Pan sprawls in front of an eagle, flanked by a female personification of Death, who holds a bloody sword, and one of Pain, who wears a crown of thorns. For 10 points each:', - bonuses[0].leadin - ); - } - { - const number = await getNumPackets('2018 PACE NSC'); - console.assert(number === 25, `${number} != 25`); - } + { + const question = 'Besides his treatise on the Divine Names, the most notable work by Pseudo-Dionysius the Areopagite discusses these things. The phrase "Grigori" refers to some of these things that are heavily described in the apocryphal Books of Enoch. First Corinthians 11 argues that, specifically because of these things, women should wear head coverings when praying or prophesying. Tertullian suggested these things are what created the gigantic Nephilim. In the Talmud, Elisha ben Abuyah declares that there are "two powers in heaven" when he sees one of these things named Metatron. The book of Daniel mentions one of these beings by name, saying he will help fight the princes of Persia and protect Israel. For 10 points, name these celestial figures that include Gabriel and Michael.'; + const answer = 'angels [or archangels; or fallen angels; or Watchers; or mal\'akhim; or Grigori until it is read]'; + const leadin = 'In a painting by this artist, a heavily-garlanded Pan sprawls in front of an eagle, flanked by a female personification of Death, who holds a bloody sword, and one of Pain, who wears a crown of thorns. For 10 points each:'; + testPacketorSet("getSet - 2016 NASAT", { setName: '2016 NASAT', packetNumbers: packetNumbers }, tossups, bonuses, 336, 336, question, answer, leadin); + } - { - const number = await getNumPackets('2016 NASAT'); - console.assert(number === 16, `${number} != 16`); - } + it("getNumPackets - 2018 PACE NSC", async () => assert.equal(await getNumPackets('2018 PACE NSC'), 25)); + it("getNumPackets - 2016 NASAT", async () => assert.equal(await getNumPackets('2016 NASAT'), 16)); + }); } (async () => { // wait for the database to connect await new Promise(resolve => setTimeout(resolve, 2000)); - console.time('database.test.js'); console.log(); console.log('Begin correctness tests'); From ede9ae92ac943ecb441417acc32ff5f9d80dc06e Mon Sep 17 00:00:00 2001 From: CaptainQuack <104166150+Captain-Quack@users.noreply.github.com> Date: Mon, 26 Jun 2023 20:25:04 -0500 Subject: [PATCH 05/24] Mochafication of server tests. --- tests/scorer.test.js | 66 +++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 40 deletions(-) diff --git a/tests/scorer.test.js b/tests/scorer.test.js index 57963e424..703648848 100644 --- a/tests/scorer.test.js +++ b/tests/scorer.test.js @@ -2,61 +2,47 @@ import checkAnswer from '../server/checkAnswer.js'; import * as bcolors from '../bcolors.js'; import { createRequire } from 'module'; - const require = createRequire(import.meta.url); const tests = require('./scorer.test.json'); - +const assert = require('chai').assert; function errorText(text) { // colors text red return `${bcolors.FAIL}${text}${bcolors.ENDC}`; } - -function testAnswerline(group) { - const answerline = group.answerline; +function answerlineTest(group) { let successful = 0, total = 0; - - group.tests.forEach(test => { - const expected = test.directive; - const givenAnswer = test.given; - const expectedDirectedPrompt = test.directedPrompt; - - const { directive, directedPrompt } = checkAnswer(answerline, givenAnswer); - - const eqAnswer = expected === directive; - - total++; - - console.assert(eqAnswer, errorText(`expected "${expected}" but got "${directive}" for given answer "${givenAnswer}"`)); - if (!eqAnswer) return; - - if (expectedDirectedPrompt || directedPrompt) { - const eqPrompt = expectedDirectedPrompt === directedPrompt; - console.assert(eqPrompt, errorText(`expected directed prompt "${expectedDirectedPrompt}" but got "${directedPrompt}" for given answer "${givenAnswer}"`)); - if (!eqPrompt) return; - } - - successful++; + const answerline = group.answerline; + describe(`Answerline Test: ${answerline}`, function() { + group.tests.forEach(test => { + const expected = test.directive; + const givenAnswer = test.given; + const expectedDirectedPrompt = test.directedPrompt; + const { directive, directedPrompt } = checkAnswer(answerline, givenAnswer); + total++; + // assertions will *supposedly* auto return when this fails. + it("directive check", ()=> assert.strictEqual(expected, directive, errorText(`directive for ${givenAnswer}`))); + if (expectedDirectedPrompt || directedPrompt) { + it("directive prompt check", ()=> assert.strictEqual(expectedDirectedPrompt, directedPrompt, errorText(`directive prompt for ${givenAnswer}`))); + } + successful++; + }); }); - - return { successful, total }; + return { successful, total }; } function testAnswerType(type, count = -1) { - console.log(`TESTING scorer.checkAnswer() for ${type} answerlines`); let successful = 0, total = 0; - - if (count > 0) - tests[type].splice(count); - - tests[type].forEach(group => { - const { successful: s, total: t } = testAnswerline(group); - successful += s; - total += t; + describe(`${type} Answer Testing`, function() { + if (count > 0) + tests[type].splice(count); + tests[type].forEach((group) => { + const { successful: s, total: t } = answerlineTest(group); + successful += s; + total += t; + }); }); - console.log(`${successful}/${total} tests successful\n`); - return { successful, total }; } From d0a4df67bd8f5bfa4d5f2603e5d0e65af4bf30fa Mon Sep 17 00:00:00 2001 From: CaptainQuack <104166150+Captain-Quack@users.noreply.github.com> Date: Mon, 26 Jun 2023 21:01:52 -0500 Subject: [PATCH 06/24] Bullied by ESLint - Part 1 --- tests/database.test.js | 115 +++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/tests/database.test.js b/tests/database.test.js index 928713043..8d52339aa 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -2,62 +2,64 @@ import { getQuery, getPacket, getSet, getRandomBonuses, getRandomTossups, getNum const assert = require('chai').assert; const packetNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]; -/* +/* Note: this.timeout(n) asserts that each `it` block individually takes less then "n" millisecconds. It's inherited by the nested test suites, and can be overriden. */ -function testTiming(count) { -return describe(`Performance Tests with ${count} repetitions`, function() { -/* -the "formula" for the timeing was done by replicating the request on the website, +async function testTiming(count) { + return describe(`Performance Tests with ${count} repetitions`, ()=> { + /* +The "formula" for the timeing was done by replicating the request on the website, and multiplying the execution time by 2 or 3 (usually), and the "count" parameter */ - function testRequest(name, timeout, func, params = false) { - it(`${name} (under ${timeout * count}ms)`, async function() { + function testRequest(name, timeout, func, params = false) { + it(`${name} (under ${timeout * count}ms)`, async ()=> { this.timeout(timeout * count); + const results = []; + // Cool trick that Eslint suggested. for (let i = 0; i < count; i++) { - await func(params); - } + results.push(func(params)); + } + await Promise.all(results); + }); } - } - describe("getQuery", function() { - testRequest("empty string", 2000, getQuery, { questionType: 'all', verbose: false}); - testRequest("'abc'", 3000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false }); - testRequest("'abc', max length 401", 5000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false, maxReturnLength: 401 }); - testRequest("'([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', regex", 10000, getQuery, { queryString: '([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', questionType: 'all', verbose: false, regex: true }); - testRequest("'cesare', ignore diacritics", 170000, getQuery, { queryString: 'cesaire', questionType: 'all', verbose: false, ignoreDiacritics: true }); - }); - describe("getPacket", function() { - testRequest("2018 PACE NSC", 1000, getPacket, {setName: '2018 PACE NSC', packetNumber: 5 }); - }); - describe("getSet", function() { - testRequest('2018 PACE NSC', 1000, getSet, { setName: '2018 PACE NSC', packetNumbers, questionType: 'bonus' }); - testRequest('Invalid set name', 2500, getSet, { setName: '( ̄y▽ ̄)╭', packetNumbers, questionType: 'bonus' }); - }); - describe("Random Functions", function() { - this.timeout(2500) - testRequest("getRandomBonuses", 2500, getRandomBonuses); - testRequest("getRandomTossups", 2500, getRandomTossups); - }); - // The report function can't use tests requests because it requires more then one parameter :( - describe("reportQuestion", function() { - it("reportQuestion", async function() { - for (let i = 0; i < count; i++) { - await reportQuestion('630020e3cab8fa6d1490b8ea', 'other', 'test'); - } + describe("getQuery", ()=> { + testRequest("empty string", 2000, getQuery, { questionType: 'all', verbose: false}); + testRequest("'abc'", 3000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false }); + testRequest("'abc', max length 401", 5000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false, maxReturnLength: 401 }); + testRequest("'([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', regex", 10000, getQuery, { queryString: '([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', questionType: 'all', verbose: false, regex: true }); + testRequest("'cesare', ignore diacritics", 170000, getQuery, { queryString: 'cesaire', questionType: 'all', verbose: false, ignoreDiacritics: true }); + }); + describe("getPacket", ()=> { + testRequest("2018 PACE NSC", 1000, getPacket, {setName: '2018 PACE NSC', packetNumber: 5 }); + }); + describe("getSet", ()=> { + testRequest('2018 PACE NSC', 1000, getSet, { setName: '2018 PACE NSC', packetNumbers, questionType: 'bonus' }); + testRequest('Invalid set name', 2500, getSet, { setName: '( ̄y▽ ̄)╭', packetNumbers, questionType: 'bonus' }); + }); + describe("Random Functions", ()=> { + this.timeout(2500); + testRequest("getRandomBonuses", 2500, getRandomBonuses); + testRequest("getRandomTossups", 2500, getRandomTossups); + }); + // The report function can't use tests requests because it requires more then one parameter :( + describe("reportQuestion", ()=> { + it("reportQuestion", async ()=> { + const results = []; + for (let i = 0; i < count; i++) { + results.push(reportQuestion('630020e3cab8fa6d1490b8ea', 'other', 'test')); + } + await Promise.all(results); + }); }); }); - -}); } - -async function testCorrectness() -{ - return describe("Correctness Tests", function() { - async function testQuery(testName, params, tossupCount, bonusCount, expectedFirstTossupQueston, expectedFirstTossupAnswer) { - it(testName, async function() { +async function testCorrectness() { + return describe("Correctness Tests", ()=> { + function testQuery(testName, params, tossupCount, bonusCount, expectedFirstTossupQueston, expectedFirstTossupAnswer) { + it(testName, async ()=> { const { tossups, bonuses } = await getQuery(params); assert.isOk(tossups, "tossups"); assert.isOk(bonuses, "bonuses"); @@ -70,22 +72,21 @@ async function testCorrectness() { const question = 'Note to moderator: Read the answerline carefully. A simplified, secular form of this practice is nicknamed “the 24.” Arthur Rosenfeld hosted a PBS program that instructed this practice for longevity and taught that chewing food 36 times can enhance the sensitivity, or “listening power,” outlined in this practice’s “classics.” The last Saturday in April is a worldwide holiday for this practice, whose methods of silk reeling and pushing hands may be attributed to its legendary inventor Zhāng Sānfēng (“jahng sahn-fung”) of the Wǔdāng (“oo-dahng”) Mountains. The Sūn (“swun”) and Yáng lineages are two of the five major styles of this type of nèijiā (“nay-jʼyah”), which originated in Chén (“chun”) Village. Unlike repetitive qìgōng (“chee-gong”), this balance-promoting practice’s “frames” link up to 108 specific postures. For 10 points, the elderly in Kowloon Park often perform what internal martial art whose routines feature slow movements?'; const answer = 'tai chi [or tàijíquán or t’ai chi ch’üan; accept shadowboxing; prompt on Chinese martial arts until read; prompt on wǔshù or guóshù or kuoshu; prompt on exercise, physical activity, or meditation; prompt on nèijiā or nèigōng or nèijìng until “nèijiā” is read; prompt on qìgōng, ch‘i kung, chi gung, or chi ‘ung until “qìgōng” is read; prompt on Wǔdāng quán until read; prompt on traditional Chinese medicine or TCM or Zhōngyī; reject “boxing”]'; - testQuery("getQuery - 'qigong', 2023 ACF Regionals, ignore diacritics", - { queryString: 'qigong', setName: '2023 ACF Regionals', verbose: false, ignoreDiacritics: true }, 1, 0, question, answer); + testQuery("getQuery - 'qigong', 2023 ACF Regionals, ignore diacritics", + { queryString: 'qigong', setName: '2023 ACF Regionals', verbose: false, ignoreDiacritics: true }, 1, 0, question, answer); } - { - const { tossups, bonuses } = await getQuery({ queryString: 'newton', questionType: 'all', setName: '2018 PACE NSC', verbose: false, maxReturnLength: 400 }); + { const question = 'A theorem introduced by this man gives a formula to find the radii of four mutually tangent circles. The second book of a work by this mathematician consists of a classification of algebraic curves, including his namesake "folium." This man is the inventor, and sometimes the namesake, of the field of analytic geometry. This man\'s three (*) "laws of nature" were a major influence on Isaac Newton\'s laws of motion. An upper limit on the number of positive roots of a polynomial can be found using this mathematician\'s "rule of signs." In two dimensions, ordered pairs are used to represent the x- and y-coordinates of numbers in his namesake coordinate system. For 10 points, name this French mathematician, who, in a famous work of philosophy, stated "Cogito ergo sum."'; const answer = 'René Descartes (day-CART)'; - testQuery("getQuery - 'newton', all questions, 2018 PACE NSC, return length = 400", + testQuery("getQuery - 'newton', all questions, 2018 PACE NSC, return length = 400", { queryString: 'newton', questionType: 'all', setName: '2018 PACE NSC', verbose: false, maxReturnLength: 400 }, - 5, 2, question, answer); + 5, 2, question, answer); } - async function testPacketorSet(testName, params, tossupCount, bonusCount, expectedFirstTossupQueston, expectedFirstTossupAnswer, expectedFirstLeadin) { - it(testName, async function() { + function testPacketorSet(testName, params, tossupCount, bonusCount, expectedFirstTossupQueston, expectedFirstTossupAnswer, expectedFirstLeadin) { + it(testName, async ()=> { const tossups = await getSet({ ...params, questionType: 'tossup' }); const bonuses = await getSet({ ...params, questionType: 'bonus' }); - + assert.isOk(tossups, "tossups"); assert.isOk(bonuses, "bonuses"); assert.propertyVal(tossups, 'length', tossupCount, "tossup count"); @@ -99,15 +100,15 @@ async function testCorrectness() const question = 'In his final appearance, this character experiences a severe toothache after asserting "as a weapon I may be of some use. But as a man, I\'m a wreck," then leaves to join King Milan\'s forces. This man buys a painting of two boys fishing, and commissions a portrait, from his fellow expatriate Mihailov. He is shocked to learn that his lover is pregnant between one scene in which he glimpses his rival Makhotin\'s chestnut (*) Gladiator, and another scene in which he rides his own horse Frou-Frou to death. This character first encounters his future lover at a railway station, where a worker is crushed by a train, and is initially interested in Kitty Shcherbatsky. For 10 points, name this Leo Tolstoy character, a nobleman who has an affair with Anna Karenina.'; const answer = 'Count Alexei (Kirillovich) Vronsky [prompt on Alexei]'; const leadin = 'The 170 men who rowed each of these ships often came from Piraeus and were thetes, the lowest class of citizen. For 10 points each:'; - testPacketorSet("getPacket -\n 2018 PACE NSC, Packet 5", { setName: '2018 PACE NSC', packetNumber: 5 } tossups, bonuses, 21, 21, question, answer, leadin); + testPacketorSet("getPacket -\n 2018 PACE NSC, Packet 5", { setName: '2018 PACE NSC', packetNumber: 5 }, 21, 21, question, answer, leadin); } { const question = 'Besides his treatise on the Divine Names, the most notable work by Pseudo-Dionysius the Areopagite discusses these things. The phrase "Grigori" refers to some of these things that are heavily described in the apocryphal Books of Enoch. First Corinthians 11 argues that, specifically because of these things, women should wear head coverings when praying or prophesying. Tertullian suggested these things are what created the gigantic Nephilim. In the Talmud, Elisha ben Abuyah declares that there are "two powers in heaven" when he sees one of these things named Metatron. The book of Daniel mentions one of these beings by name, saying he will help fight the princes of Persia and protect Israel. For 10 points, name these celestial figures that include Gabriel and Michael.'; const answer = 'angels [or archangels; or fallen angels; or Watchers; or mal\'akhim; or Grigori until it is read]'; const leadin = 'In a painting by this artist, a heavily-garlanded Pan sprawls in front of an eagle, flanked by a female personification of Death, who holds a bloody sword, and one of Pain, who wears a crown of thorns. For 10 points each:'; - testPacketorSet("getSet - 2016 NASAT", { setName: '2016 NASAT', packetNumbers: packetNumbers }, tossups, bonuses, 336, 336, question, answer, leadin); - } + testPacketorSet("getSet - 2016 NASAT", { setName: '2016 NASAT', packetNumbers: packetNumbers }, 336, 336, question, answer, leadin); + } it("getNumPackets - 2018 PACE NSC", async () => assert.equal(await getNumPackets('2018 PACE NSC'), 25)); it("getNumPackets - 2016 NASAT", async () => assert.equal(await getNumPackets('2016 NASAT'), 16)); @@ -115,8 +116,8 @@ async function testCorrectness() } (async () => { - // wait for the database to connect - await new Promise(resolve => setTimeout(resolve, 2000)); + // Wait for the database to connect + await new Promise((resolve) => setTimeout(resolve, 2000)); console.time('database.test.js'); console.log(); console.log('Begin correctness tests'); From d35811b09bbd3d4e7bc60a806f52b2cff2f5705f Mon Sep 17 00:00:00 2001 From: CaptainQuack <104166150+Captain-Quack@users.noreply.github.com> Date: Mon, 26 Jun 2023 21:06:44 -0500 Subject: [PATCH 07/24] Bullied by ESLint - Part 2 --- tests/scorer.test.js | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/tests/scorer.test.js b/tests/scorer.test.js index 703648848..512b1773c 100644 --- a/tests/scorer.test.js +++ b/tests/scorer.test.js @@ -1,46 +1,48 @@ import checkAnswer from '../server/checkAnswer.js'; import * as bcolors from '../bcolors.js'; - +const { describe, it } = require("mocha"); import { createRequire } from 'module'; const require = createRequire(import.meta.url); const tests = require('./scorer.test.json'); -const assert = require('chai').assert; -function errorText(text) { // colors text red +const assert = require('chai').assert; +function errorText(text) { + // Colors text red return `${bcolors.FAIL}${text}${bcolors.ENDC}`; } -function answerlineTest(group) { +function answerlineTest(group) { let successful = 0, total = 0; const answerline = group.answerline; - describe(`Answerline Test: ${answerline}`, function() { - group.tests.forEach(test => { + describe(`Answerline Test: ${answerline}`, ()=> { + group.tests.forEach((test) => { const expected = test.directive; const givenAnswer = test.given; const expectedDirectedPrompt = test.directedPrompt; const { directive, directedPrompt } = checkAnswer(answerline, givenAnswer); total++; - // assertions will *supposedly* auto return when this fails. - it("directive check", ()=> assert.strictEqual(expected, directive, errorText(`directive for ${givenAnswer}`))); + // Assertions will *supposedly* auto return when this fails. + it("directive check", ()=> assert.strictEqual(expected, directive, errorText(`directive for ${givenAnswer}`))); if (expectedDirectedPrompt || directedPrompt) { it("directive prompt check", ()=> assert.strictEqual(expectedDirectedPrompt, directedPrompt, errorText(`directive prompt for ${givenAnswer}`))); } successful++; }); }); - return { successful, total }; + return { successful, total }; } function testAnswerType(type, count = -1) { let successful = 0, total = 0; - describe(`${type} Answer Testing`, function() { - if (count > 0) - tests[type].splice(count); - tests[type].forEach((group) => { - const { successful: s, total: t } = answerlineTest(group); - successful += s; - total += t; - }); + describe(`${type} Answer Testing`, ()=> { + if (count > 0) { + tests[type].splice(count); + } + tests[type].forEach((group) => { + const { successful: s, total: t } = answerlineTest(group); + successful += s; + total += t; + }); }); console.log(`${successful}/${total} tests successful\n`); return { successful, total }; From 1620119cb73ec7f4f94df29112bb2d91be569f42 Mon Sep 17 00:00:00 2001 From: CaptainQuack <104166150+Captain-Quack@users.noreply.github.com> Date: Mon, 26 Jun 2023 21:48:31 -0500 Subject: [PATCH 08/24] Linter wanted single quotes --- tests/database.test.js | 66 +++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/tests/database.test.js b/tests/database.test.js index 8d52339aa..b2a26f1e9 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -3,7 +3,7 @@ const assert = require('chai').assert; const packetNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]; /* - Note: this.timeout(n) asserts that each `it` block individually takes less then "n" millisecconds. + Note: this.timeout(n) asserts that each `it` block individually takes less then n millisecconds. It's inherited by the nested test suites, and can be overriden. */ @@ -24,28 +24,28 @@ and multiplying the execution time by 2 or 3 (usually), and the "count" paramete await Promise.all(results); }); } - describe("getQuery", ()=> { - testRequest("empty string", 2000, getQuery, { questionType: 'all', verbose: false}); - testRequest("'abc'", 3000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false }); - testRequest("'abc', max length 401", 5000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false, maxReturnLength: 401 }); - testRequest("'([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', regex", 10000, getQuery, { queryString: '([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', questionType: 'all', verbose: false, regex: true }); - testRequest("'cesare', ignore diacritics", 170000, getQuery, { queryString: 'cesaire', questionType: 'all', verbose: false, ignoreDiacritics: true }); + describe('getQuery', ()=> { + testRequest('empty string', 2000, getQuery, { questionType: 'all', verbose: false}); + testRequest('"abc"', 3000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false }); + testRequest('"abc"', max length 401", 5000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false, maxReturnLength: 401 }); + testRequest('"([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}"', regex", 10000, getQuery, { queryString: '([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', questionType: 'all', verbose: false, regex: true }); + testRequest('"cesare", ignore diacritics"', 170000, getQuery, { queryString: 'cesaire', questionType: 'all', verbose: false, ignoreDiacritics: true }); }); - describe("getPacket", ()=> { - testRequest("2018 PACE NSC", 1000, getPacket, {setName: '2018 PACE NSC', packetNumber: 5 }); + describe('getPacket', ()=> { + testRequest('2018 PACE NSC', 1000, getPacket, {setName: '2018 PACE NSC', packetNumber: 5 }); }); - describe("getSet", ()=> { + describe('getSet', ()=> { testRequest('2018 PACE NSC', 1000, getSet, { setName: '2018 PACE NSC', packetNumbers, questionType: 'bonus' }); testRequest('Invalid set name', 2500, getSet, { setName: '( ̄y▽ ̄)╭', packetNumbers, questionType: 'bonus' }); }); - describe("Random Functions", ()=> { + describe('Random Functions', ()=> { this.timeout(2500); testRequest("getRandomBonuses", 2500, getRandomBonuses); testRequest("getRandomTossups", 2500, getRandomTossups); }); // The report function can't use tests requests because it requires more then one parameter :( - describe("reportQuestion", ()=> { - it("reportQuestion", async ()=> { + describe('reportQuestion', ()=> { + it('reportQuestion', async ()=> { const results = []; for (let i = 0; i < count; i++) { results.push(reportQuestion('630020e3cab8fa6d1490b8ea', 'other', 'test')); @@ -57,28 +57,28 @@ and multiplying the execution time by 2 or 3 (usually), and the "count" paramete } async function testCorrectness() { - return describe("Correctness Tests", ()=> { + return describe('Correctness Tests', ()=> { function testQuery(testName, params, tossupCount, bonusCount, expectedFirstTossupQueston, expectedFirstTossupAnswer) { it(testName, async ()=> { const { tossups, bonuses } = await getQuery(params); - assert.isOk(tossups, "tossups"); - assert.isOk(bonuses, "bonuses"); - assert.propertyVal(tossups, 'count', tossupCount, "tossup count"); - assert.propertyVal(bonuses, 'count', bonusCount, "bonus count"); - assert.strictEqual(tossups.questionArray[0].question, expectedFirstTossupQueston, "tossup array - question"); - assert.strictEqual(tossups.questionArray[0].answer, expectedFirstTossupAnswer, "tossup array - answer"); + assert.isOk(tossups, 'tossups'); + assert.isOk(bonuses, 'bonuses'); + assert.propertyVal(tossups, 'count', tossupCount, 'tossup count'); + assert.propertyVal(bonuses, 'count', bonusCount, 'bonus count'); + assert.strictEqual(tossups.questionArray[0].question, expectedFirstTossupQueston, 'tossup array - question'); + assert.strictEqual(tossups.questionArray[0].answer, expectedFirstTossupAnswer, 'tossup array - answer'); }); } { const question = 'Note to moderator: Read the answerline carefully. A simplified, secular form of this practice is nicknamed “the 24.” Arthur Rosenfeld hosted a PBS program that instructed this practice for longevity and taught that chewing food 36 times can enhance the sensitivity, or “listening power,” outlined in this practice’s “classics.” The last Saturday in April is a worldwide holiday for this practice, whose methods of silk reeling and pushing hands may be attributed to its legendary inventor Zhāng Sānfēng (“jahng sahn-fung”) of the Wǔdāng (“oo-dahng”) Mountains. The Sūn (“swun”) and Yáng lineages are two of the five major styles of this type of nèijiā (“nay-jʼyah”), which originated in Chén (“chun”) Village. Unlike repetitive qìgōng (“chee-gong”), this balance-promoting practice’s “frames” link up to 108 specific postures. For 10 points, the elderly in Kowloon Park often perform what internal martial art whose routines feature slow movements?'; const answer = 'tai chi [or tàijíquán or t’ai chi ch’üan; accept shadowboxing; prompt on Chinese martial arts until read; prompt on wǔshù or guóshù or kuoshu; prompt on exercise, physical activity, or meditation; prompt on nèijiā or nèigōng or nèijìng until “nèijiā” is read; prompt on qìgōng, ch‘i kung, chi gung, or chi ‘ung until “qìgōng” is read; prompt on Wǔdāng quán until read; prompt on traditional Chinese medicine or TCM or Zhōngyī; reject “boxing”]'; - testQuery("getQuery - 'qigong', 2023 ACF Regionals, ignore diacritics", + testQuery('getQuery - "qigong", 2023 ACF Regionals, ignore diacritics', { queryString: 'qigong', setName: '2023 ACF Regionals', verbose: false, ignoreDiacritics: true }, 1, 0, question, answer); } { const question = 'A theorem introduced by this man gives a formula to find the radii of four mutually tangent circles. The second book of a work by this mathematician consists of a classification of algebraic curves, including his namesake "folium." This man is the inventor, and sometimes the namesake, of the field of analytic geometry. This man\'s three (*) "laws of nature" were a major influence on Isaac Newton\'s laws of motion. An upper limit on the number of positive roots of a polynomial can be found using this mathematician\'s "rule of signs." In two dimensions, ordered pairs are used to represent the x- and y-coordinates of numbers in his namesake coordinate system. For 10 points, name this French mathematician, who, in a famous work of philosophy, stated "Cogito ergo sum."'; const answer = 'René Descartes (day-CART)'; - testQuery("getQuery - 'newton', all questions, 2018 PACE NSC, return length = 400", + testQuery('getQuery - "newton", all questions, 2018 PACE NSC, return length = 400', { queryString: 'newton', questionType: 'all', setName: '2018 PACE NSC', verbose: false, maxReturnLength: 400 }, 5, 2, question, answer); } @@ -87,31 +87,31 @@ async function testCorrectness() { const tossups = await getSet({ ...params, questionType: 'tossup' }); const bonuses = await getSet({ ...params, questionType: 'bonus' }); - assert.isOk(tossups, "tossups"); - assert.isOk(bonuses, "bonuses"); - assert.propertyVal(tossups, 'length', tossupCount, "tossup count"); - assert.propertyVal(bonuses, 'length', bonusCount, "bonus count"); - assert.propertyVal(bonuses[0], 'leadin', expectedFirstLeadin, "bonuses - leadins"); - assert.strictEqual(tossups[0].question, expectedFirstTossupQueston, "tossups - question"); - assert.strictEqual(tossups[0].answer, expectedFirstTossupAnswer, "tossups - answer"); + assert.isOk(tossups, 'tossups'); + assert.isOk(bonuses, 'bonuses'); + assert.propertyVal(tossups, 'length', tossupCount, 'tossup count'); + assert.propertyVal(bonuses, 'length', bonusCount, 'bonus count'); + assert.propertyVal(bonuses[0], 'leadin', expectedFirstLeadin, 'bonuses - leadins'); + assert.strictEqual(tossups[0].question, expectedFirstTossupQueston, 'tossups - question'); + assert.strictEqual(tossups[0].answer, expectedFirstTossupAnswer, 'tossups - answer'); }); } { const question = 'In his final appearance, this character experiences a severe toothache after asserting "as a weapon I may be of some use. But as a man, I\'m a wreck," then leaves to join King Milan\'s forces. This man buys a painting of two boys fishing, and commissions a portrait, from his fellow expatriate Mihailov. He is shocked to learn that his lover is pregnant between one scene in which he glimpses his rival Makhotin\'s chestnut (*) Gladiator, and another scene in which he rides his own horse Frou-Frou to death. This character first encounters his future lover at a railway station, where a worker is crushed by a train, and is initially interested in Kitty Shcherbatsky. For 10 points, name this Leo Tolstoy character, a nobleman who has an affair with Anna Karenina.'; const answer = 'Count Alexei (Kirillovich) Vronsky [prompt on Alexei]'; const leadin = 'The 170 men who rowed each of these ships often came from Piraeus and were thetes, the lowest class of citizen. For 10 points each:'; - testPacketorSet("getPacket -\n 2018 PACE NSC, Packet 5", { setName: '2018 PACE NSC', packetNumber: 5 }, 21, 21, question, answer, leadin); + testPacketorSet('getPacket -\n 2018 PACE NSC, Packet 5', { setName: '2018 PACE NSC', packetNumber: 5 }, 21, 21, question, answer, leadin); } { const question = 'Besides his treatise on the Divine Names, the most notable work by Pseudo-Dionysius the Areopagite discusses these things. The phrase "Grigori" refers to some of these things that are heavily described in the apocryphal Books of Enoch. First Corinthians 11 argues that, specifically because of these things, women should wear head coverings when praying or prophesying. Tertullian suggested these things are what created the gigantic Nephilim. In the Talmud, Elisha ben Abuyah declares that there are "two powers in heaven" when he sees one of these things named Metatron. The book of Daniel mentions one of these beings by name, saying he will help fight the princes of Persia and protect Israel. For 10 points, name these celestial figures that include Gabriel and Michael.'; const answer = 'angels [or archangels; or fallen angels; or Watchers; or mal\'akhim; or Grigori until it is read]'; const leadin = 'In a painting by this artist, a heavily-garlanded Pan sprawls in front of an eagle, flanked by a female personification of Death, who holds a bloody sword, and one of Pain, who wears a crown of thorns. For 10 points each:'; - testPacketorSet("getSet - 2016 NASAT", { setName: '2016 NASAT', packetNumbers: packetNumbers }, 336, 336, question, answer, leadin); + testPacketorSet('getSet - 2016 NASAT', { setName: '2016 NASAT', packetNumbers: packetNumbers }, 336, 336, question, answer, leadin); } - it("getNumPackets - 2018 PACE NSC", async () => assert.equal(await getNumPackets('2018 PACE NSC'), 25)); - it("getNumPackets - 2016 NASAT", async () => assert.equal(await getNumPackets('2016 NASAT'), 16)); + it('getNumPackets - 2018 PACE NSC', async () => assert.equal(await getNumPackets('2018 PACE NSC'), 25)); + it('getNumPackets - 2016 NASAT', async () => assert.equal(await getNumPackets('2016 NASAT'), 16)); }); } From c6d4e2f20ba69b9622d45e5e111601e9599228ce Mon Sep 17 00:00:00 2001 From: CaptainQuack <104166150+Captain-Quack@users.noreply.github.com> Date: Mon, 26 Jun 2023 21:57:39 -0500 Subject: [PATCH 09/24] Replacing More Double Quotes --- tests/scorer.test.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/scorer.test.js b/tests/scorer.test.js index 512b1773c..1703ab9c3 100644 --- a/tests/scorer.test.js +++ b/tests/scorer.test.js @@ -1,6 +1,5 @@ import checkAnswer from '../server/checkAnswer.js'; import * as bcolors from '../bcolors.js'; -const { describe, it } = require("mocha"); import { createRequire } from 'module'; const require = createRequire(import.meta.url); const tests = require('./scorer.test.json'); @@ -21,9 +20,9 @@ function answerlineTest(group) { const { directive, directedPrompt } = checkAnswer(answerline, givenAnswer); total++; // Assertions will *supposedly* auto return when this fails. - it("directive check", ()=> assert.strictEqual(expected, directive, errorText(`directive for ${givenAnswer}`))); + it('directive check', ()=> assert.strictEqual(expected, directive, errorText(`directive for ${givenAnswer}`))); if (expectedDirectedPrompt || directedPrompt) { - it("directive prompt check", ()=> assert.strictEqual(expectedDirectedPrompt, directedPrompt, errorText(`directive prompt for ${givenAnswer}`))); + it('directive prompt check', ()=> assert.strictEqual(expectedDirectedPrompt, directedPrompt, errorText(`directive prompt for ${givenAnswer}`))); } successful++; }); From 36c84250fa6a82d98f352d1c8a467af1edd44d11 Mon Sep 17 00:00:00 2001 From: CaptainQuack <104166150+Captain-Quack@users.noreply.github.com> Date: Mon, 26 Jun 2023 21:59:48 -0500 Subject: [PATCH 10/24] oops --- tests/database.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/database.test.js b/tests/database.test.js index b2a26f1e9..886f9c9b1 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -27,7 +27,7 @@ and multiplying the execution time by 2 or 3 (usually), and the "count" paramete describe('getQuery', ()=> { testRequest('empty string', 2000, getQuery, { questionType: 'all', verbose: false}); testRequest('"abc"', 3000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false }); - testRequest('"abc"', max length 401", 5000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false, maxReturnLength: 401 }); + testRequest('"abc"', maxLength 401", 5000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false, maxReturnLength: 401 }); testRequest('"([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}"', regex", 10000, getQuery, { queryString: '([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', questionType: 'all', verbose: false, regex: true }); testRequest('"cesare", ignore diacritics"', 170000, getQuery, { queryString: 'cesaire', questionType: 'all', verbose: false, ignoreDiacritics: true }); }); From 1f6298e13665b164f58b3f025a61bfdd80d9b268 Mon Sep 17 00:00:00 2001 From: CaptainQuack <104166150+Captain-Quack@users.noreply.github.com> Date: Mon, 26 Jun 2023 22:04:07 -0500 Subject: [PATCH 11/24] oops (take 2) --- tests/database.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/database.test.js b/tests/database.test.js index 886f9c9b1..ea6906c2b 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -27,7 +27,7 @@ and multiplying the execution time by 2 or 3 (usually), and the "count" paramete describe('getQuery', ()=> { testRequest('empty string', 2000, getQuery, { questionType: 'all', verbose: false}); testRequest('"abc"', 3000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false }); - testRequest('"abc"', maxLength 401", 5000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false, maxReturnLength: 401 }); + testRequest('"abc", return length 401', 5000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false, maxReturnLength: 401 }); testRequest('"([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}"', regex", 10000, getQuery, { queryString: '([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', questionType: 'all', verbose: false, regex: true }); testRequest('"cesare", ignore diacritics"', 170000, getQuery, { queryString: 'cesaire', questionType: 'all', verbose: false, ignoreDiacritics: true }); }); From b75820e267fb58c89b30cac2c39e74b78a093556 Mon Sep 17 00:00:00 2001 From: CaptainQuack <104166150+Captain-Quack@users.noreply.github.com> Date: Mon, 26 Jun 2023 22:08:06 -0500 Subject: [PATCH 12/24] Oops - The Phantom Menace it would be nice to recive these errors all at once... --- tests/database.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/database.test.js b/tests/database.test.js index ea6906c2b..f5558ad9b 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -28,7 +28,7 @@ and multiplying the execution time by 2 or 3 (usually), and the "count" paramete testRequest('empty string', 2000, getQuery, { questionType: 'all', verbose: false}); testRequest('"abc"', 3000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false }); testRequest('"abc", return length 401', 5000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false, maxReturnLength: 401 }); - testRequest('"([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}"', regex", 10000, getQuery, { queryString: '([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', questionType: 'all', verbose: false, regex: true }); + testRequest('"([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}"', regex, 10000, getQuery, { queryString: '([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', questionType: 'all', verbose: false, regex: true }); testRequest('"cesare", ignore diacritics"', 170000, getQuery, { queryString: 'cesaire', questionType: 'all', verbose: false, ignoreDiacritics: true }); }); describe('getPacket', ()=> { From cfbd5a73ae693369400569b245a5ab6bbbd8bdbf Mon Sep 17 00:00:00 2001 From: CaptainQuack <104166150+Captain-Quack@users.noreply.github.com> Date: Mon, 26 Jun 2023 22:11:36 -0500 Subject: [PATCH 13/24] =?UTF-8?q?=F0=9F=A4=9Elast=20quote=20error=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/database.test.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/database.test.js b/tests/database.test.js index f5558ad9b..e67e79992 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -25,14 +25,14 @@ and multiplying the execution time by 2 or 3 (usually), and the "count" paramete }); } describe('getQuery', ()=> { - testRequest('empty string', 2000, getQuery, { questionType: 'all', verbose: false}); + testRequest('empty string', 2000, getQuery, { questionType: 'all', verbose: false }); testRequest('"abc"', 3000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false }); testRequest('"abc", return length 401', 5000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false, maxReturnLength: 401 }); - testRequest('"([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}"', regex, 10000, getQuery, { queryString: '([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', questionType: 'all', verbose: false, regex: true }); + testRequest('"([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}", regex', 10000, getQuery, { queryString: '([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', questionType: 'all', verbose: false, regex: true }); testRequest('"cesare", ignore diacritics"', 170000, getQuery, { queryString: 'cesaire', questionType: 'all', verbose: false, ignoreDiacritics: true }); }); describe('getPacket', ()=> { - testRequest('2018 PACE NSC', 1000, getPacket, {setName: '2018 PACE NSC', packetNumber: 5 }); + testRequest('2018 PACE NSC', 1000, getPacket, {setName: '2018 PACE NSC', packetNumber: 5 }); }); describe('getSet', ()=> { testRequest('2018 PACE NSC', 1000, getSet, { setName: '2018 PACE NSC', packetNumbers, questionType: 'bonus' }); @@ -40,8 +40,8 @@ and multiplying the execution time by 2 or 3 (usually), and the "count" paramete }); describe('Random Functions', ()=> { this.timeout(2500); - testRequest("getRandomBonuses", 2500, getRandomBonuses); - testRequest("getRandomTossups", 2500, getRandomTossups); + testRequest('getRandomBonuses', 2500, getRandomBonuses); + testRequest('getRandomTossups', 2500, getRandomTossups); }); // The report function can't use tests requests because it requires more then one parameter :( describe('reportQuestion', ()=> { From 7dd53d1f624c465d9e5b95be249b7417630e81bc Mon Sep 17 00:00:00 2001 From: CaptainQuack <104166150+Captain-Quack@users.noreply.github.com> Date: Mon, 26 Jun 2023 22:14:50 -0500 Subject: [PATCH 14/24] Require Mocha, just in case. --- tests/database.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/database.test.js b/tests/database.test.js index e67e79992..c52a411e2 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -1,5 +1,6 @@ import { getQuery, getPacket, getSet, getRandomBonuses, getRandomTossups, getNumPackets, reportQuestion } from '../database/questions'; const assert = require('chai').assert; +const { describe, it } = require("mocha"); const packetNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]; /* @@ -32,7 +33,7 @@ and multiplying the execution time by 2 or 3 (usually), and the "count" paramete testRequest('"cesare", ignore diacritics"', 170000, getQuery, { queryString: 'cesaire', questionType: 'all', verbose: false, ignoreDiacritics: true }); }); describe('getPacket', ()=> { - testRequest('2018 PACE NSC', 1000, getPacket, {setName: '2018 PACE NSC', packetNumber: 5 }); + testRequest('2018 PACE NSC', 1000, getPacket, { setName: '2018 PACE NSC', packetNumber: 5 }); }); describe('getSet', ()=> { testRequest('2018 PACE NSC', 1000, getSet, { setName: '2018 PACE NSC', packetNumbers, questionType: 'bonus' }); From 18fc59523f11921c15b6e4f943249ceb0c7092b8 Mon Sep 17 00:00:00 2001 From: CaptainQuack <104166150+Captain-Quack@users.noreply.github.com> Date: Mon, 26 Jun 2023 22:15:27 -0500 Subject: [PATCH 15/24] Require Mocha, just in case. --- tests/scorer.test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/scorer.test.js b/tests/scorer.test.js index 1703ab9c3..1b83270c0 100644 --- a/tests/scorer.test.js +++ b/tests/scorer.test.js @@ -4,6 +4,8 @@ import { createRequire } from 'module'; const require = createRequire(import.meta.url); const tests = require('./scorer.test.json'); const assert = require('chai').assert; +const { describe, it } = require('mocha'); + function errorText(text) { // Colors text red return `${bcolors.FAIL}${text}${bcolors.ENDC}`; From d9e8fd89ea92f29e5ed5d583f089a4e58e1186e8 Mon Sep 17 00:00:00 2001 From: CaptainQuack <104166150+Captain-Quack@users.noreply.github.com> Date: Mon, 26 Jun 2023 22:16:33 -0500 Subject: [PATCH 16/24] darn double quotes --- tests/database.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/database.test.js b/tests/database.test.js index c52a411e2..fdf0474cf 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -1,6 +1,6 @@ import { getQuery, getPacket, getSet, getRandomBonuses, getRandomTossups, getNumPackets, reportQuestion } from '../database/questions'; const assert = require('chai').assert; -const { describe, it } = require("mocha"); +const { describe, it } = require('mocha'); const packetNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]; /* From 61df6b44c1b034bb0c2e2312e7b1c80698baa5e6 Mon Sep 17 00:00:00 2001 From: Geoffrey Wu Date: Sat, 1 Jul 2023 10:42:49 -0400 Subject: [PATCH 17/24] Update package-lock.json --- package-lock.json | 882 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 882 insertions(+) diff --git a/package-lock.json b/package-lock.json index 585869f4d..988c1fccd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,8 +34,10 @@ "@babel/cli": "^7.19.3", "@babel/core": "^7.20.2", "@babel/preset-react": "^7.18.6", + "chai": "^4.3.7", "eslint": "^8.28.0", "eslint-plugin-react": "^7.31.11", + "mocha": "^10.2.0", "sass": "^1.56.2" } }, @@ -984,6 +986,15 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1100,6 +1111,15 @@ "get-intrinsic": "^1.1.3" } }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1202,6 +1222,12 @@ "node": ">=8" } }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, "node_modules/browserslist": { "version": "4.21.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", @@ -1298,6 +1324,18 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001434", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", @@ -1314,6 +1352,24 @@ } ] }, + "node_modules/chai": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^4.1.2", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1330,6 +1386,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -1369,6 +1434,17 @@ "node": ">= 6" } }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1551,11 +1627,35 @@ "ms": "2.0.0" } }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/decimal.js": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1610,6 +1710,15 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1665,6 +1774,12 @@ "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "dev": true }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -2260,6 +2375,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -2375,6 +2499,24 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/get-intrinsic": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", @@ -2524,6 +2666,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, "node_modules/html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -2825,6 +2976,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2882,6 +3042,15 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -2945,6 +3114,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -3174,6 +3355,22 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -3185,6 +3382,15 @@ "loose-envify": "cli.js" } }, + "node_modules/loupe": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -3278,6 +3484,164 @@ "node": "*" } }, + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/mongodb": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.8.0.tgz", @@ -3309,6 +3673,18 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -3604,6 +3980,15 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -3715,6 +4100,15 @@ } ] }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/range_check": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/range_check/-/range_check-2.0.4.tgz", @@ -3819,6 +4213,15 @@ "url": "https://github.com/sponsors/mysticatea" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -4026,6 +4429,15 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", @@ -4148,6 +4560,20 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", @@ -4340,6 +4766,15 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -4563,6 +4998,29 @@ "node": ">=0.10.0" } }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -4602,11 +5060,62 @@ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -5318,6 +5827,12 @@ "uri-js": "^4.2.2" } }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -5409,6 +5924,12 @@ "get-intrinsic": "^1.1.3" } }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -5475,6 +5996,12 @@ "fill-range": "^7.0.1" } }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, "browserslist": { "version": "4.21.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", @@ -5529,12 +6056,33 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, "caniuse-lite": { "version": "1.0.30001434", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==", "dev": true }, + "chai": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^4.1.2", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5545,6 +6093,12 @@ "supports-color": "^7.1.0" } }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true + }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -5572,6 +6126,17 @@ } } }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -5728,11 +6293,26 @@ "ms": "2.0.0" } }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, "decimal.js": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" }, + "deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -5768,6 +6348,12 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -5814,6 +6400,12 @@ "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "dev": true }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -6264,6 +6856,12 @@ "path-exists": "^4.0.0" } }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -6348,6 +6946,18 @@ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true + }, "get-intrinsic": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", @@ -6449,6 +7059,12 @@ "has-symbols": "^1.0.2" } }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, "html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -6658,6 +7274,12 @@ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -6694,6 +7316,12 @@ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, "is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -6736,6 +7364,12 @@ "has-symbols": "^1.0.2" } }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, "is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -6915,6 +7549,16 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -6923,6 +7567,15 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "loupe": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -6989,6 +7642,126 @@ "brace-expansion": "^1.1.7" } }, + "mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "requires": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "mongodb": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.8.0.tgz", @@ -7015,6 +7788,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -7229,6 +8008,12 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -7302,6 +8087,15 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, "range_check": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/range_check/-/range_check-2.0.4.tgz", @@ -7376,6 +8170,12 @@ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -7517,6 +8317,15 @@ } } }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", @@ -7603,6 +8412,17 @@ "resolved": "https://registry.npmjs.org/stemmer/-/stemmer-2.0.1.tgz", "integrity": "sha512-bkWvSX2JR4nSZFfs113kd4C6X13bBBrg4fBKv2pVdzpdQI2LA5pZcWzTFNdkYsiUNl13E4EzymSRjZ0D55jBYg==" }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "string.prototype.matchall": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", @@ -7746,6 +8566,12 @@ "prelude-ls": "^1.2.1" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -7898,6 +8724,23 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" }, + "workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -7920,11 +8763,50 @@ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", From 8fe42f047f11062dc9bc2a969f8e6733231e9982 Mon Sep 17 00:00:00 2001 From: Geoffrey Wu Date: Sat, 1 Jul 2023 10:49:45 -0400 Subject: [PATCH 18/24] use es modules --- tests/database.test.js | 39 ++++++++++++++++++++------------------- tests/scorer.test.js | 14 ++++++++------ 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/tests/database.test.js b/tests/database.test.js index fdf0474cf..8dc0b2559 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -1,6 +1,7 @@ -import { getQuery, getPacket, getSet, getRandomBonuses, getRandomTossups, getNumPackets, reportQuestion } from '../database/questions'; -const assert = require('chai').assert; -const { describe, it } = require('mocha'); +import { getQuery, getPacket, getSet, getRandomBonuses, getRandomTossups, getNumPackets, reportQuestion } from '../database/questions.js'; +import { assert } from 'chai'; +import mocha from 'mocha'; + const packetNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]; /* @@ -9,13 +10,13 @@ const packetNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 */ async function testTiming(count) { - return describe(`Performance Tests with ${count} repetitions`, ()=> { + return mocha.describe(`Performance Tests with ${count} repetitions`, ()=> { /* -The "formula" for the timeing was done by replicating the request on the website, -and multiplying the execution time by 2 or 3 (usually), and the "count" parameter -*/ + The "formula" for the timeing was done by replicating the request on the website, + and multiplying the execution time by 2 or 3 (usually), and the "count" parameter + */ function testRequest(name, timeout, func, params = false) { - it(`${name} (under ${timeout * count}ms)`, async ()=> { + mocha.it(`${name} (under ${timeout * count}ms)`, async ()=> { this.timeout(timeout * count); const results = []; // Cool trick that Eslint suggested. @@ -25,28 +26,28 @@ and multiplying the execution time by 2 or 3 (usually), and the "count" paramete await Promise.all(results); }); } - describe('getQuery', ()=> { + mocha.describe('getQuery', ()=> { testRequest('empty string', 2000, getQuery, { questionType: 'all', verbose: false }); testRequest('"abc"', 3000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false }); testRequest('"abc", return length 401', 5000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false, maxReturnLength: 401 }); testRequest('"([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}", regex', 10000, getQuery, { queryString: '([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', questionType: 'all', verbose: false, regex: true }); testRequest('"cesare", ignore diacritics"', 170000, getQuery, { queryString: 'cesaire', questionType: 'all', verbose: false, ignoreDiacritics: true }); }); - describe('getPacket', ()=> { + mocha.describe('getPacket', ()=> { testRequest('2018 PACE NSC', 1000, getPacket, { setName: '2018 PACE NSC', packetNumber: 5 }); }); - describe('getSet', ()=> { + mocha.describe('getSet', ()=> { testRequest('2018 PACE NSC', 1000, getSet, { setName: '2018 PACE NSC', packetNumbers, questionType: 'bonus' }); testRequest('Invalid set name', 2500, getSet, { setName: '( ̄y▽ ̄)╭', packetNumbers, questionType: 'bonus' }); }); - describe('Random Functions', ()=> { + mocha.describe('Random Functions', ()=> { this.timeout(2500); testRequest('getRandomBonuses', 2500, getRandomBonuses); testRequest('getRandomTossups', 2500, getRandomTossups); }); // The report function can't use tests requests because it requires more then one parameter :( - describe('reportQuestion', ()=> { - it('reportQuestion', async ()=> { + mocha.describe('reportQuestion', ()=> { + mocha.it('reportQuestion', async ()=> { const results = []; for (let i = 0; i < count; i++) { results.push(reportQuestion('630020e3cab8fa6d1490b8ea', 'other', 'test')); @@ -58,9 +59,9 @@ and multiplying the execution time by 2 or 3 (usually), and the "count" paramete } async function testCorrectness() { - return describe('Correctness Tests', ()=> { + return mocha.describe('Correctness Tests', ()=> { function testQuery(testName, params, tossupCount, bonusCount, expectedFirstTossupQueston, expectedFirstTossupAnswer) { - it(testName, async ()=> { + mocha.it(testName, async ()=> { const { tossups, bonuses } = await getQuery(params); assert.isOk(tossups, 'tossups'); assert.isOk(bonuses, 'bonuses'); @@ -84,7 +85,7 @@ async function testCorrectness() { 5, 2, question, answer); } function testPacketorSet(testName, params, tossupCount, bonusCount, expectedFirstTossupQueston, expectedFirstTossupAnswer, expectedFirstLeadin) { - it(testName, async ()=> { + mocha.it(testName, async ()=> { const tossups = await getSet({ ...params, questionType: 'tossup' }); const bonuses = await getSet({ ...params, questionType: 'bonus' }); @@ -111,8 +112,8 @@ async function testCorrectness() { testPacketorSet('getSet - 2016 NASAT', { setName: '2016 NASAT', packetNumbers: packetNumbers }, 336, 336, question, answer, leadin); } - it('getNumPackets - 2018 PACE NSC', async () => assert.equal(await getNumPackets('2018 PACE NSC'), 25)); - it('getNumPackets - 2016 NASAT', async () => assert.equal(await getNumPackets('2016 NASAT'), 16)); + mocha.it('getNumPackets - 2018 PACE NSC', async () => assert.equal(await getNumPackets('2018 PACE NSC'), 25)); + mocha.it('getNumPackets - 2016 NASAT', async () => assert.equal(await getNumPackets('2016 NASAT'), 16)); }); } diff --git a/tests/scorer.test.js b/tests/scorer.test.js index 1b83270c0..b305a23c5 100644 --- a/tests/scorer.test.js +++ b/tests/scorer.test.js @@ -1,10 +1,12 @@ import checkAnswer from '../server/checkAnswer.js'; import * as bcolors from '../bcolors.js'; + +import { assert } from 'chai'; +import mocha from 'mocha'; import { createRequire } from 'module'; + const require = createRequire(import.meta.url); const tests = require('./scorer.test.json'); -const assert = require('chai').assert; -const { describe, it } = require('mocha'); function errorText(text) { // Colors text red @@ -14,7 +16,7 @@ function errorText(text) { function answerlineTest(group) { let successful = 0, total = 0; const answerline = group.answerline; - describe(`Answerline Test: ${answerline}`, ()=> { + mocha.describe(`Answerline Test: ${answerline}`, ()=> { group.tests.forEach((test) => { const expected = test.directive; const givenAnswer = test.given; @@ -22,9 +24,9 @@ function answerlineTest(group) { const { directive, directedPrompt } = checkAnswer(answerline, givenAnswer); total++; // Assertions will *supposedly* auto return when this fails. - it('directive check', ()=> assert.strictEqual(expected, directive, errorText(`directive for ${givenAnswer}`))); + mocha.it('directive check', () => assert.strictEqual(expected, directive, errorText(`directive for ${givenAnswer}`))); if (expectedDirectedPrompt || directedPrompt) { - it('directive prompt check', ()=> assert.strictEqual(expectedDirectedPrompt, directedPrompt, errorText(`directive prompt for ${givenAnswer}`))); + mocha.it('directive prompt check', () => assert.strictEqual(expectedDirectedPrompt, directedPrompt, errorText(`directive prompt for ${givenAnswer}`))); } successful++; }); @@ -35,7 +37,7 @@ function answerlineTest(group) { function testAnswerType(type, count = -1) { let successful = 0, total = 0; - describe(`${type} Answer Testing`, ()=> { + mocha.describe(`${type} Answer Testing`, () => { if (count > 0) { tests[type].splice(count); } From f892fb988620f07e3518942851c66fcdaca081ee Mon Sep 17 00:00:00 2001 From: Geoffrey Wu Date: Sat, 1 Jul 2023 10:58:25 -0400 Subject: [PATCH 19/24] remove redundant logging --- tests/scorer.test.js | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/tests/scorer.test.js b/tests/scorer.test.js index b305a23c5..47f5131ae 100644 --- a/tests/scorer.test.js +++ b/tests/scorer.test.js @@ -48,25 +48,10 @@ function testAnswerType(type, count = -1) { }); }); console.log(`${successful}/${total} tests successful\n`); - return { successful, total }; + return successful === total; } -console.time('scorer.test.js'); -let successful = 0, total = 0; - const count = -1; -let { successful: s, total: t } = testAnswerType('formatted', count); -successful += s; -total += t; - -({ successful: s, total: t } = testAnswerType('unformatted', count)); -successful += s; -total += t; - -console.log(`OVERALL ${successful}/${total} tests successful`); -console.timeEnd('scorer.test.js'); - -if (successful !== total) { - process.exit(1); -} +testAnswerType('formatted', count); +testAnswerType('unformatted', count); From 9ffa52cc855457c244dbfd0b45914ed89c648b66 Mon Sep 17 00:00:00 2001 From: Geoffrey Wu Date: Sat, 1 Jul 2023 11:17:49 -0400 Subject: [PATCH 20/24] fix correctness tests and waiting for database --- database/questions.js | 8 ++++-- tests/database.test.js | 62 +++++++++++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/database/questions.js b/database/questions.js index 66d0f7d58..868b18e19 100644 --- a/database/questions.js +++ b/database/questions.js @@ -5,10 +5,13 @@ import { MongoClient, ObjectId } from 'mongodb'; const uri = `mongodb+srv://${process.env.MONGODB_USERNAME || 'geoffreywu42'}:${process.env.MONGODB_PASSWORD || 'password'}@qbreader.0i7oej9.mongodb.net/?retryWrites=true&w=majority`; const client = new MongoClient(uri); -client.connect().then(async () => { + +async function connectToDatabase() { + await client.connect(); console.log('connected to mongodb'); -}); +} +await connectToDatabase(); const database = client.db('qbreader'); @@ -590,6 +593,7 @@ async function reportQuestion(_id, reason, description, verbose = true) { export { + connectToDatabase, getNumPackets, getPacket, getQuery, diff --git a/tests/database.test.js b/tests/database.test.js index 8dc0b2559..53dc527ad 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -1,4 +1,6 @@ -import { getQuery, getPacket, getSet, getRandomBonuses, getRandomTossups, getNumPackets, reportQuestion } from '../database/questions.js'; +import 'dotenv/config'; + +import { connectToDatabase, getQuery, getPacket, getSet, getRandomBonuses, getRandomTossups, getNumPackets, reportQuestion } from '../database/questions.js'; import { assert } from 'chai'; import mocha from 'mocha'; @@ -59,7 +61,9 @@ async function testTiming(count) { } async function testCorrectness() { - return mocha.describe('Correctness Tests', ()=> { + return mocha.describe('Correctness Tests', function () { + this.timeout(0); + function testQuery(testName, params, tossupCount, bonusCount, expectedFirstTossupQueston, expectedFirstTossupAnswer) { mocha.it(testName, async ()=> { const { tossups, bonuses } = await getQuery(params); @@ -84,10 +88,12 @@ async function testCorrectness() { { queryString: 'newton', questionType: 'all', setName: '2018 PACE NSC', verbose: false, maxReturnLength: 400 }, 5, 2, question, answer); } - function testPacketorSet(testName, params, tossupCount, bonusCount, expectedFirstTossupQueston, expectedFirstTossupAnswer, expectedFirstLeadin) { + + + function testGetPacket(testName, params, tossupCount, bonusCount, expectedFirstTossupQueston, expectedFirstTossupAnswer, expectedFirstLeadin) { mocha.it(testName, async ()=> { - const tossups = await getSet({ ...params, questionType: 'tossup' }); - const bonuses = await getSet({ ...params, questionType: 'bonus' }); + const packet = await getPacket({ ...params, questionType: 'tossup' }); + const { tossups, bonuses } = packet; assert.isOk(tossups, 'tossups'); assert.isOk(bonuses, 'bonuses'); @@ -102,14 +108,29 @@ async function testCorrectness() { const question = 'In his final appearance, this character experiences a severe toothache after asserting "as a weapon I may be of some use. But as a man, I\'m a wreck," then leaves to join King Milan\'s forces. This man buys a painting of two boys fishing, and commissions a portrait, from his fellow expatriate Mihailov. He is shocked to learn that his lover is pregnant between one scene in which he glimpses his rival Makhotin\'s chestnut (*) Gladiator, and another scene in which he rides his own horse Frou-Frou to death. This character first encounters his future lover at a railway station, where a worker is crushed by a train, and is initially interested in Kitty Shcherbatsky. For 10 points, name this Leo Tolstoy character, a nobleman who has an affair with Anna Karenina.'; const answer = 'Count Alexei (Kirillovich) Vronsky [prompt on Alexei]'; const leadin = 'The 170 men who rowed each of these ships often came from Piraeus and were thetes, the lowest class of citizen. For 10 points each:'; - testPacketorSet('getPacket -\n 2018 PACE NSC, Packet 5', { setName: '2018 PACE NSC', packetNumber: 5 }, 21, 21, question, answer, leadin); + testGetPacket('getPacket -\n 2018 PACE NSC, Packet 5', { setName: '2018 PACE NSC', packetNumber: 5 }, 21, 21, question, answer, leadin); + } + + function testGetSet(testName, params, tossupCount, bonusCount, expectedFirstTossupQueston, expectedFirstTossupAnswer, expectedFirstLeadin) { + mocha.it(testName, async ()=> { + const tossups = await getSet({ ...params, questionType: 'tossup' }); + const bonuses = await getSet({ ...params, questionType: 'bonus' }); + + assert.isOk(tossups, 'tossups'); + assert.isOk(bonuses, 'bonuses'); + assert.propertyVal(tossups, 'length', tossupCount, 'tossup count'); + assert.propertyVal(bonuses, 'length', bonusCount, 'bonus count'); + assert.propertyVal(bonuses[0], 'leadin', expectedFirstLeadin, 'bonuses - leadins'); + assert.strictEqual(tossups[0].question, expectedFirstTossupQueston, 'tossups - question'); + assert.strictEqual(tossups[0].answer, expectedFirstTossupAnswer, 'tossups - answer'); + }); } { const question = 'Besides his treatise on the Divine Names, the most notable work by Pseudo-Dionysius the Areopagite discusses these things. The phrase "Grigori" refers to some of these things that are heavily described in the apocryphal Books of Enoch. First Corinthians 11 argues that, specifically because of these things, women should wear head coverings when praying or prophesying. Tertullian suggested these things are what created the gigantic Nephilim. In the Talmud, Elisha ben Abuyah declares that there are "two powers in heaven" when he sees one of these things named Metatron. The book of Daniel mentions one of these beings by name, saying he will help fight the princes of Persia and protect Israel. For 10 points, name these celestial figures that include Gabriel and Michael.'; const answer = 'angels [or archangels; or fallen angels; or Watchers; or mal\'akhim; or Grigori until it is read]'; const leadin = 'In a painting by this artist, a heavily-garlanded Pan sprawls in front of an eagle, flanked by a female personification of Death, who holds a bloody sword, and one of Pain, who wears a crown of thorns. For 10 points each:'; - testPacketorSet('getSet - 2016 NASAT', { setName: '2016 NASAT', packetNumbers: packetNumbers }, 336, 336, question, answer, leadin); + testGetSet('getSet - 2016 NASAT', { setName: '2016 NASAT', packetNumbers: packetNumbers }, 336, 336, question, answer, leadin); } mocha.it('getNumPackets - 2018 PACE NSC', async () => assert.equal(await getNumPackets('2018 PACE NSC'), 25)); @@ -117,19 +138,16 @@ async function testCorrectness() { }); } -(async () => { +mocha.before(async () => { // Wait for the database to connect - await new Promise((resolve) => setTimeout(resolve, 2000)); - console.time('database.test.js'); - console.log(); - console.log('Begin correctness tests'); - await testCorrectness(); - console.log('End correctness tests'); - console.log(); - - console.log('Begin timing tests'); - await testTiming(5); - console.log('End timing tests'); - console.log(); - console.timeEnd('database.test.js'); -})(); + // await new Promise((resolve) => setTimeout(resolve, 1500)); + await connectToDatabase(); +}); + + +testCorrectness(); + +// console.log('Begin timing tests'); +// await testTiming(5); +// console.log('End timing tests'); +// console.log(); From 9a7969208b700a0e1c1a6a5f304d8f3b7a014022 Mon Sep 17 00:00:00 2001 From: Geoffrey Wu Date: Sat, 1 Jul 2023 11:21:09 -0400 Subject: [PATCH 21/24] don't always log connected string --- database/questions.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/database/questions.js b/database/questions.js index 868b18e19..db632b3ba 100644 --- a/database/questions.js +++ b/database/questions.js @@ -6,12 +6,15 @@ import { MongoClient, ObjectId } from 'mongodb'; const uri = `mongodb+srv://${process.env.MONGODB_USERNAME || 'geoffreywu42'}:${process.env.MONGODB_PASSWORD || 'password'}@qbreader.0i7oej9.mongodb.net/?retryWrites=true&w=majority`; const client = new MongoClient(uri); -async function connectToDatabase() { +async function connectToDatabase(log=false) { await client.connect(); - console.log('connected to mongodb'); + + if (log) { + console.log('connected to mongodb'); + } } -await connectToDatabase(); +await connectToDatabase(true); const database = client.db('qbreader'); From 81b5c3754c8ca4e96974946cdd391328d0859f52 Mon Sep 17 00:00:00 2001 From: Geoffrey Wu Date: Sat, 1 Jul 2023 11:21:25 -0400 Subject: [PATCH 22/24] fix this.timeout in test timing --- tests/database.test.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/database.test.js b/tests/database.test.js index 53dc527ad..cccee1176 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -12,13 +12,13 @@ const packetNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 */ async function testTiming(count) { - return mocha.describe(`Performance Tests with ${count} repetitions`, ()=> { + return mocha.describe(`Performance Tests with ${count} repetitions`, function () { /* The "formula" for the timeing was done by replicating the request on the website, and multiplying the execution time by 2 or 3 (usually), and the "count" parameter */ function testRequest(name, timeout, func, params = false) { - mocha.it(`${name} (under ${timeout * count}ms)`, async ()=> { + mocha.it(`${name} (under ${timeout * count}ms)`, async function () { this.timeout(timeout * count); const results = []; // Cool trick that Eslint suggested. @@ -140,14 +140,13 @@ async function testCorrectness() { mocha.before(async () => { // Wait for the database to connect - // await new Promise((resolve) => setTimeout(resolve, 1500)); await connectToDatabase(); }); testCorrectness(); +testTiming(5); // console.log('Begin timing tests'); -// await testTiming(5); // console.log('End timing tests'); // console.log(); From 323f614e20357bf6bc7c2805698ee7696ac78d41 Mon Sep 17 00:00:00 2001 From: Geoffrey Wu Date: Sat, 1 Jul 2023 11:29:24 -0400 Subject: [PATCH 23/24] tighten timing requirements --- tests/database.test.js | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/tests/database.test.js b/tests/database.test.js index cccee1176..7c03a4ee7 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -20,32 +20,28 @@ async function testTiming(count) { function testRequest(name, timeout, func, params = false) { mocha.it(`${name} (under ${timeout * count}ms)`, async function () { this.timeout(timeout * count); - const results = []; - // Cool trick that Eslint suggested. for (let i = 0; i < count; i++) { - results.push(func(params)); + await func(params); } - await Promise.all(results); }); } mocha.describe('getQuery', ()=> { - testRequest('empty string', 2000, getQuery, { questionType: 'all', verbose: false }); - testRequest('"abc"', 3000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false }); - testRequest('"abc", return length 401', 5000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false, maxReturnLength: 401 }); - testRequest('"([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}", regex', 10000, getQuery, { queryString: '([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', questionType: 'all', verbose: false, regex: true }); - testRequest('"cesare", ignore diacritics"', 170000, getQuery, { queryString: 'cesaire', questionType: 'all', verbose: false, ignoreDiacritics: true }); + testRequest('empty string', 800, getQuery, { questionType: 'all', verbose: false }); + testRequest('"abc"', 2000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false }); + testRequest('"abc", return length 401', 3000, getQuery, { queryString: 'abc', questionType: 'all', verbose: false, maxReturnLength: 401 }); + testRequest('"([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}", regex', 5000, getQuery, { queryString: '([aàáâǎäãåāăạả](b*)[cçćčɔ́ĉƈ]+?.*){1,}', questionType: 'all', verbose: false, regex: true }); + testRequest('"cesare", ignore diacritics"', 8000, getQuery, { queryString: 'cesaire', questionType: 'all', verbose: false, ignoreDiacritics: true }); }); mocha.describe('getPacket', ()=> { - testRequest('2018 PACE NSC', 1000, getPacket, { setName: '2018 PACE NSC', packetNumber: 5 }); + testRequest('2018 PACE NSC', 400, getPacket, { setName: '2018 PACE NSC', packetNumber: 5 }); }); mocha.describe('getSet', ()=> { testRequest('2018 PACE NSC', 1000, getSet, { setName: '2018 PACE NSC', packetNumbers, questionType: 'bonus' }); - testRequest('Invalid set name', 2500, getSet, { setName: '( ̄y▽ ̄)╭', packetNumbers, questionType: 'bonus' }); + testRequest('Invalid set name', 100, getSet, { setName: '( ̄y▽ ̄)╭', packetNumbers, questionType: 'bonus' }); }); - mocha.describe('Random Functions', ()=> { - this.timeout(2500); - testRequest('getRandomBonuses', 2500, getRandomBonuses); - testRequest('getRandomTossups', 2500, getRandomTossups); + mocha.describe('Random Functions', () => { + testRequest('getRandomBonuses', 2000, getRandomBonuses); + testRequest('getRandomTossups', 2000, getRandomTossups); }); // The report function can't use tests requests because it requires more then one parameter :( mocha.describe('reportQuestion', ()=> { From d2173836a0bbfd4feca780cc130b340ec5329413 Mon Sep 17 00:00:00 2001 From: Geoffrey Wu Date: Sat, 1 Jul 2023 11:30:22 -0400 Subject: [PATCH 24/24] only use one repetition for timing tests --- tests/database.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/database.test.js b/tests/database.test.js index 7c03a4ee7..c3f275090 100644 --- a/tests/database.test.js +++ b/tests/database.test.js @@ -141,7 +141,7 @@ mocha.before(async () => { testCorrectness(); -testTiming(5); +testTiming(1); // console.log('Begin timing tests'); // console.log('End timing tests');