CourseInsights / test / controller / InsightFacadeMartin.spec.ts
InsightFacadeMartin.spec.ts
Raw
import {
	IInsightFacade,
	InsightDatasetKind,
	InsightError,
	InsightResult,
	NotFoundError,
	ResultTooLargeError,
} from "../../src/controller/IInsightFacade";
import InsightFacade from "../../src/controller/InsightFacade";
import { clearDisk, getContentFromArchives, loadTestQuery } from "../TestUtil";

import { expect, use } from "chai";
import chaiAsPromised from "chai-as-promised";

use(chaiAsPromised);

export interface ITestQuery {
	title?: string;
	input: unknown;
	errorExpected: boolean;
	expected: any;
	orderExpected: boolean;
	orderBy: string;
}

describe("InsightFacadeMartin", function () {
	// Declare datasets used in tests. You should add more datasets like this!
	let facade: IInsightFacade;
	let sections: string;
	let emptySections: string;
	let bSyntax: string;
	let coursess: string;
	let noResult: string;

	before(async function () {
		// This block runs once and loads the datasets.
		sections = await getContentFromArchives("pair.zip");
		emptySections = await getContentFromArchives("empty_sections.zip");
		bSyntax = await getContentFromArchives("badSyntax.zip");
		coursess = await getContentFromArchives("coursess.zip");
		noResult = await getContentFromArchives("no_result.zip");

		// Just in case there is anything hanging around from a previous run of the test suite
		await clearDisk();
	});

	describe("AddDataset", function () {
		beforeEach(async function () {
			await clearDisk();
			facade = new InsightFacade();
		});

		// it("should reject with an empty dataset id", async function () {
		// 	try {
		// 		await facade.addDataset("", sections, InsightDatasetKind.Sections);
		// 		expect.fail("Should have thrown!");
		// 	} catch (err) {
		// 		expect(err).to.be.instanceOf(InsightError);
		// 	}
		// });

		// it("should reject with an empty space dataset id", async function () {
		// 	try {
		// 		await facade.addDataset(" ", sections, InsightDatasetKind.Sections);
		// 		expect.fail("Should have thrown!");
		// 	} catch (err) {
		// 		expect(err).to.be.instanceOf(InsightError);
		// 	}
		// });

		// it("should add dataset", async function () {
		// 	let result = [];
		// 	try {
		// 		result = await facade.addDataset("ubc", sections, InsightDatasetKind.Sections);
		// 		expect(result.length).to.be.equal(1);
		// 		expect(result).to.have.members(["ubc"]);
		// 	} catch {
		// 		expect.fail();
		// 	}
		// });

		// it("should add both datasets", async function () {
		// 	let result = [];
		// 	try {
		// 		await facade.addDataset("ubc1", sections, InsightDatasetKind.Sections);
		// 		result = await facade.addDataset("ubc2", sections, InsightDatasetKind.Sections);
		// 		expect(result).to.have.members(["ubc1", "ubc2"]);
		// 	} catch {
		// 		expect.fail();
		// 	}
		// });

		// it("should fail add same id twice", async function () {
		// 	try {
		// 		await facade.addDataset("ubc1", sections, InsightDatasetKind.Sections);
		// 		await facade.addDataset("ubc1", sections, InsightDatasetKind.Sections);
		// 		expect.fail();
		// 	} catch (e) {
		// 		expect(e).to.be.instanceOf(InsightError);
		// 	}
		// });

		// it("should fail, add same id twice then fails again for same reason", async function () {
		// 	try {
		// 		await facade.addDataset("ubc1", sections, InsightDatasetKind.Sections);
		// 		await facade.addDataset("ubc1", sections, InsightDatasetKind.Sections);
		// 		expect.fail();
		// 	} catch (e) {
		// 		expect(e).to.be.instanceOf(InsightError);
		// 		const result = await facade.listDatasets();
		// 		expect(result.length).to.be.equal(1);
		// 	}
		// });

		it("should reject with empty dataset", async function () {
			try {
				await facade.addDataset("ubc", emptySections, InsightDatasetKind.Sections);
				expect.fail();
			} catch (e) {
				expect(e).to.be.instanceOf(InsightError);
			}
		});

		it("should reject with wrong folder name", async function () {
			try {
				await facade.addDataset("ubc", coursess, InsightDatasetKind.Sections);
				expect.fail();
			} catch (e) {
				expect(e).to.be.instanceOf(InsightError);
			}
		});

		it("should reject with dataset bad syntax json", async function () {
			try {
				await facade.addDataset("ubc", bSyntax, InsightDatasetKind.Sections);
				expect.fail();
			} catch (e) {
				expect(e).to.be.instanceOf(InsightError);
			}
		});

		it("should reject with dataset has no json result", async function () {
			try {
				await facade.addDataset("ubc", noResult, InsightDatasetKind.Sections);
				expect.fail();
			} catch (e) {
				expect(e).to.be.instanceOf(InsightError);
			}
		});

		// it("should reject with an _ id", async function () {
		// 	try {
		// 		await facade.addDataset("_", sections, InsightDatasetKind.Sections);
		// 		expect.fail("Should have thrown!");
		// 	} catch (err) {
		// 		expect(err).to.be.instanceOf(InsightError);
		// 	}
		// });

		// it("should reject id contains _", async function () {
		// 	try {
		// 		await facade.addDataset("ubc_2025", sections, InsightDatasetKind.Sections);
		// 		expect.fail("Should have thrown!");
		// 	} catch (err) {
		// 		expect(err).to.be.instanceOf(InsightError);
		// 	}
		// });

		it("should reject id contains wrong database type", async function () {
			try {
				await facade.addDataset("ubc_2025", "hello World!", InsightDatasetKind.Sections);
				expect.fail("Should have thrown!");
			} catch (err) {
				expect(err).to.be.instanceOf(InsightError);
			}
		});
	});

	describe("RemoveDataset", function () {
		beforeEach(async function () {
			await clearDisk();
			facade = new InsightFacade();
		});

		it("should reject, no such dataset", async function () {
			try {
				await facade.removeDataset("ubc");
				expect.fail("error not expected");
			} catch (e) {
				expect(e).to.be.instanceOf(NotFoundError);
			}
		});

		// it("should reject, wrong dataset id", async function () {
		// 	try {
		// 		await facade.addDataset("ubcc", sections, InsightDatasetKind.Sections);
		// 		await facade.removeDataset("ubc");
		// 		expect.fail("error not expected");
		// 	} catch (e) {
		// 		expect(e).to.be.instanceOf(NotFoundError);
		// 	}
		// });

		// it("should reject, empty string id", async function () {
		// 	try {
		// 		await facade.removeDataset("");
		// 		expect.fail("error not expected");
		// 	} catch (e) {
		// 		expect(e).to.be.instanceOf(InsightError);
		// 	}
		// });

		it("should reject empty space string id", async function () {
			try {
				await facade.removeDataset(" ");
				expect.fail("error not expected");
			} catch (e) {
				expect(e).to.be.instanceOf(InsightError);
			}
		});

		it("should reject _ id", async function () {
			try {
				await facade.removeDataset("_");
				expect.fail("error not expected");
			} catch (e) {
				expect(e).to.be.instanceOf(InsightError);
			}
		});

		// it("should reject ubc_ id", async function () {
		// 	try {
		// 		await facade.removeDataset("ubc_");
		// 		expect.fail("error not expected");
		// 	} catch (e) {
		// 		expect(e).to.be.instanceOf(InsightError);
		// 	}
		// });

		// it("should remove the dataset", async function () {
		// 	try {
		// 		await facade.addDataset("ubc", sections, InsightDatasetKind.Sections);
		// 		const result = await facade.removeDataset("ubc");
		// 		expect(result).to.be.equal("ubc");
		// 		const result2 = await facade.listDatasets();
		// 		expect(result2.length).to.be.equal(0);
		// 	} catch {
		// 		expect.fail("error not expected");
		// 	}
		// });

		// it("should add 2 remove 1", async function () {
		// 	let result = "";
		// 	try {
		// 		await facade.addDataset("ubc1", sections, InsightDatasetKind.Sections);
		// 		await facade.addDataset("ubc2", sections, InsightDatasetKind.Sections);
		// 		result = await facade.removeDataset("ubc1");
		// 		expect(result).to.be.equal("ubc1");
		// 		//const result2 = await facade.addDataset("ubc3", sections, InsightDatasetKind.Sections);
		// 		//expect(result2).to.have.members(["ubc2", "ubc3"]);
		// 	} catch {
		// 		expect.fail("error not expected");
		// 	}
		// });

		// it("should add 2 remove 2", async function () {
		// 	let result = "";
		// 	try {
		// 		await facade.addDataset("ubc1", sections, InsightDatasetKind.Sections);
		// 		await facade.addDataset("ubc2", sections, InsightDatasetKind.Sections);
		// 		result = await facade.removeDataset("ubc1");
		// 		expect(result).to.be.equal("ubc1");
		// 		result = await facade.removeDataset("ubc2");
		// 		expect(result).to.be.equal("ubc2");
		// 		const result2 = await facade.listDatasets();
		// 		expect(result2.length).to.be.equal(0);
		// 	} catch {
		// 		expect.fail("error not expected");
		// 	}
		// });
	});

	// describe("ListDatasets", function () {
	// 	beforeEach(async function () {
	// 		await clearDisk();
	// 		facade = new InsightFacade();
	// 	});

	// 	it("should produce an empty list", async function () {
	// 		let result = [];
	// 		result = await facade.listDatasets();
	// 		expect(result.length).to.be.equal(0);
	// 	});

	// 	it("should produce a list length 1", async function () {
	// 		try {
	// 			await facade.addDataset("ubc", sections, InsightDatasetKind.Sections);
	// 			const result = await facade.listDatasets();
	// 			expect(result.length).to.be.equal(1);
	// 		} catch {
	// 			expect.fail();
	// 		}
	// 	});

	// 	it("should produce a list length 2", async function () {
	// 		try {
	// 			await facade.addDataset("ubc1", sections, InsightDatasetKind.Sections);
	// 			await facade.addDataset("ubc2", sections, InsightDatasetKind.Sections);
	// 		} catch {
	// 			expect.fail();
	// 		}
	// 	});
	// });

	describe("PerformQuery", function () {
		/**
		 * Loads the TestQuery specified in the test name and asserts the behaviour of performQuery.
		 *
		 * Note: the 'this' parameter is automatically set by Mocha and contains information about the test.
		 */
		async function checkQuery(this: Mocha.Context): Promise<void> {
			if (!this.test) {
				throw new Error(
					"Invalid call to checkQuery." +
						"Usage: 'checkQuery' must be passed as the second parameter of Mocha's it(..) function." +
						"Do not invoke the function directly."
				);
			}
			// Destructuring assignment to reduce property accesses
			const { input, expected, errorExpected, orderExpected, orderBy } = await loadTestQuery(this.test.title);
			let result: InsightResult[] = []; // dummy value before being reassigned
			try {
				result = await facade.performQuery(input);
			} catch (err) {
				if (!errorExpected) {
					expect.fail(`performQuery threw unexpected error: ${err}`);
				}
				// check what error is expected and then make sure that is the actual error received
				if ("InsightError" === expected) {
					expect(err).to.be.instanceOf(InsightError);
				} else {
					expect(err).to.be.instanceOf(ResultTooLargeError);
				}
				return;
			}
			if (errorExpected) {
				expect.fail(`performQuery resolved when it should have rejected with ${expected}`);
			}
			// make sure output is the expected result
			//expect(result).to.deep.equal(expected);
			expect(result).to.have.deep.members(expected);
			expect(result).to.have.length(expected.length);
			if (orderExpected) {
				const resultAvg = result.map((item) => orderBy.map((key) => item[key]));
				const expectedAvg = expected.map((item: InsightResult) => orderBy.map((key) => item[key]));
				expect(resultAvg).to.deep.equal(expectedAvg);
			}
		}

		before(async function () {
			facade = new InsightFacade();
			// Add the datasets to InsightFacade once.
			// Will *fail* if there is a problem reading ANY dataset.
			const loadDatasetPromises: Promise<string[]>[] = [
				facade.addDataset("sections", sections, InsightDatasetKind.Sections),
			];
			try {
				await Promise.all(loadDatasetPromises);
			} catch (err) {
				throw new Error(`In PerformQuery Before hook, dataset(s) failed to be added. \n${err}`);
			}
		});

		after(async function () {
			await clearDisk();
		});

		// Examples demonstrating how to test performQuery using the JSON Test Queries.
		// The relative path to the query file must be given in square brackets.
		it("[valid/avgmoreThan97AndStartsWithC.json] Select dept, avg WHERE avg>97 and dept c*", checkQuery);
		it("[valid/avgLess30.json] Select dept, avg WHERE avg < 30", checkQuery);
		it("[valid/notGt21.json] Select dept, avg WHERE  !(avg<21)", checkQuery);
		it("[valid/1example.json] returns 1 example by uuid", checkQuery);
		it("[valid/andEmpty.json] empty and", checkQuery);
		it("[valid/andNonEmpty.json] not empty and", checkQuery);
		//it("[valid/containsAt.json] Select dept, avg WHERE dept *at*", checkQuery);
		//it("[valid/containsMath.json] Select dept, avg WHERE dept *math*", checkQuery);
		//it("[valid/deptContainsSy.json] Select dept, avg WHERE dept *sy*", checkQuery);
		//it("[valid/deptEndsSc.json] Select dept, avg WHERE  dept *sc", checkQuery);
		//it("[valid/deptIsCpsc.json] Select dept, avg WHERE dept cpsc", checkQuery);
		//it("[valid/deptIsMath.json] Select dept, avg WHERE dept math", checkQuery);
		//it("[valid/deptStartsCp.json] Select dept, avg WHERE dept cp*", checkQuery);
		//it("[valid/endsTh.json] Select dept, avg WHERE dept *th", checkQuery);
		//it("[valid/startsMa.json] Select dept, avg WHERE ma*", checkQuery);
		//it("[valid/eq80.json] Select dept, avg WHERE avg = 80", checkQuery);
		//it("[valid/less68.5.json] Select dept, avg WHERE avg < 68.5", checkQuery);
		//it("[valid/more97OrStartsCpc.json] Select dept, avg WHERE avg > 97 or dept cps*", checkQuery);
		//it("[valid/usesPassOrderId.json] Select pass, id WHERE  pass > 300", checkQuery);
		//it("[valid/usesAuditOrderTitle.json] Uses Audit and Title", checkQuery);
		//it("[valid/usesFailOrderInstructor.json] Uses Fail and Instructor", checkQuery);
		//it("[valid/4999results.json] returns with 4999 results", checkQuery);
		//it("[valid/5000results.json] returns with 5000 results", checkQuery);
		//it("[valid/usesNand.json] uses Nand", checkQuery);
		//it("[valid/usesNor.json] Uses nor", checkQuery);
		//it("[valid/usesYearOrderUuid.json] Uses year and uuid", checkQuery);
		//it("[valid/orWithInter.json] or with intersection", checkQuery);

		it("[invalid/moreThan1DS.json] Query pulls from more than 1 dataset", checkQuery);
		it("[invalid/tooMany.json] Query returns more than 5000 results", checkQuery);
		it("[invalid/empty.json] returns empty results)", checkQuery);
		it("[invalid/5001results.json] fails with 5001 results", checkQuery);
		it("[invalid/invalidColumns.json] Query mispelled columns", checkQuery);
		it("[invalid/invalidkey.json] invalid key", checkQuery);
		it("[invalid/noOptions.json] Query missing OPTIONS", checkQuery);
		it("[invalid/wrongType.json] s in m field", checkQuery);
		it("[invalid/wrongType2.json] m in s field", checkQuery);
		it("[invalid/badString.json] reject has bad string syntax with *", checkQuery);
		it("[invalid/badStringSyntax.json] extra string bad case", checkQuery);
		it("[invalid/doubleAsterix.json] double**", checkQuery);
		it("[invalid/DoubleAsterixButWorse.json] 0**0", checkQuery);
		it("[invalid/extraKeyInQuery.json] extra key in query", checkQuery);
	});
});