416 lines
10 KiB
TypeScript
416 lines
10 KiB
TypeScript
import { Database } from "bun:sqlite";
|
|
import { expect, test } from "bun:test";
|
|
import { Prequel, Table } from "../src/index";
|
|
|
|
interface UserWithID {
|
|
user_id: number;
|
|
name: string;
|
|
}
|
|
|
|
interface UserWithBio {
|
|
user_id: number;
|
|
bio: {
|
|
name: string;
|
|
age: number;
|
|
};
|
|
}
|
|
|
|
interface Post {
|
|
post_id: number;
|
|
user_id: number;
|
|
}
|
|
|
|
interface UserWithNullable extends UserWithID {
|
|
bio: string | null;
|
|
}
|
|
|
|
function makeTestTable(): Table<UserWithID> {
|
|
const db = new Database();
|
|
return new Table<UserWithID>(db, "Users", {
|
|
user_id: {
|
|
type: "INTEGER",
|
|
primary: true,
|
|
},
|
|
name: "TEXT",
|
|
});
|
|
}
|
|
|
|
test("constructor", () => {
|
|
const db = new Database();
|
|
const table = new Table<UserWithID>(db, "Users", {
|
|
user_id: {
|
|
type: "INTEGER",
|
|
primary: true,
|
|
},
|
|
name: "TEXT",
|
|
});
|
|
|
|
expect(table.name).toEqual("Users");
|
|
expect(table.db).toBe(db);
|
|
expect(table.columns).toEqual(["user_id", "name"]);
|
|
});
|
|
|
|
test("constructor -> auto-attach to a prequel isntance", () => {
|
|
const db = new Prequel();
|
|
const table = new Table<UserWithID>(db, "Users", {
|
|
user_id: {
|
|
type: "INTEGER",
|
|
primary: true,
|
|
},
|
|
name: "TEXT",
|
|
});
|
|
|
|
expect(Object.values(db.tables)).toContain(table);
|
|
});
|
|
|
|
test(".toString() resolves to the table name for embedding in queries", () => {
|
|
const table = makeTestTable();
|
|
expect(`${table}`).toEqual('"Users"');
|
|
});
|
|
|
|
test(".reference() creates a default reference to the primary column", () => {
|
|
const table = makeTestTable();
|
|
expect(table.reference()).toEqual("Users(user_id)");
|
|
});
|
|
|
|
test(".reference() creates a reference to the specified column", () => {
|
|
const table = makeTestTable();
|
|
expect(table.reference("name")).toEqual("Users(name)");
|
|
});
|
|
|
|
test(".insert() inserts a full row", () => {
|
|
const table = makeTestTable();
|
|
table.insert({
|
|
user_id: 100,
|
|
name: "Jack",
|
|
});
|
|
|
|
const lookup = table.db.prepare<UserWithID, number>(
|
|
`SELECT * FROM ${table} WHERE user_id = ?`,
|
|
);
|
|
const found = lookup.get(100);
|
|
expect(found).toEqual({
|
|
user_id: 100,
|
|
name: "Jack",
|
|
});
|
|
});
|
|
|
|
test(".insert() is able to insert nulls to nullable fields", () => {
|
|
const table = new Table<UserWithNullable>(new Database(), "Users", {
|
|
user_id: {
|
|
type: "INTEGER",
|
|
primary: true,
|
|
},
|
|
name: "TEXT",
|
|
bio: {
|
|
type: "TEXT",
|
|
nullable: true,
|
|
},
|
|
});
|
|
|
|
table.insert({
|
|
user_id: 1,
|
|
name: "Jack",
|
|
bio: null,
|
|
});
|
|
|
|
const lookup = table.db.prepare<UserWithNullable, number>(
|
|
`SELECT * FROM ${table} WHERE user_id = ?`,
|
|
);
|
|
const found = lookup.get(1);
|
|
|
|
expect(found?.bio).toBeNull();
|
|
});
|
|
|
|
test(".insertPartial() inserts a partial row", () => {
|
|
const table = makeTestTable();
|
|
table.insertPartial({
|
|
name: "Jack",
|
|
});
|
|
|
|
const lookup = table.db.prepare<UserWithID, string>(
|
|
`SELECT * FROM ${table} WHERE name = ?`,
|
|
);
|
|
const found = lookup.get("Jack");
|
|
expect(found).toEqual({
|
|
user_id: 1,
|
|
name: "Jack",
|
|
});
|
|
});
|
|
|
|
test(".insertWithout() inserts a partial row with type safety", () => {
|
|
const table = makeTestTable();
|
|
table.insertWithout<"user_id">({
|
|
name: "Jack",
|
|
});
|
|
|
|
const lookup = table.db.prepare<UserWithID, string>(
|
|
`SELECT * FROM ${table} WHERE name = ?`,
|
|
);
|
|
const found = lookup.get("Jack");
|
|
expect(found).toEqual({
|
|
user_id: 1,
|
|
name: "Jack",
|
|
});
|
|
});
|
|
|
|
test(".count() gets the current number of rows in the table", () => {
|
|
const table = makeTestTable();
|
|
table.insertPartial({ name: "Jack" });
|
|
table.insertPartial({ name: "Kate" });
|
|
table.insertPartial({ name: "Sayid" });
|
|
expect(table.count()).toBe(3);
|
|
});
|
|
|
|
test(".countWhere() gets the current number of rows that match a condition", () => {
|
|
const table = makeTestTable();
|
|
table.insertPartial({ name: "Jack" });
|
|
table.insertPartial({ name: "Kate" });
|
|
table.insertPartial({ name: "Sayid" });
|
|
expect(table.countWhere({ name: "Jack" })).toBe(1);
|
|
});
|
|
|
|
test(".findAll() returns the full table", () => {
|
|
const table = makeTestTable();
|
|
table.insertPartial({ name: "Jack" });
|
|
table.insertPartial({ name: "Kate" });
|
|
table.insertPartial({ name: "Sayid" });
|
|
const allUsers = table.findAll();
|
|
|
|
expect(allUsers).toHaveLength(3);
|
|
expect(allUsers[0]).toEqual({ user_id: 1, name: "Jack" });
|
|
expect(allUsers[1]).toEqual({ user_id: 2, name: "Kate" });
|
|
expect(allUsers[2]).toEqual({ user_id: 3, name: "Sayid" });
|
|
});
|
|
|
|
test(".query() returns a prepared statement for the table", () => {
|
|
const table = makeTestTable();
|
|
const query = table.query("SELECT * FROM Users WHERE name = ?");
|
|
expect(query).toBeDefined();
|
|
});
|
|
|
|
test(".findOneWhere() returns a row based on basic criteria", () => {
|
|
const table = makeTestTable();
|
|
table.insertPartial({ name: "Jack" });
|
|
table.insertPartial({ name: "Kate" });
|
|
table.insertPartial({ name: "Sayid" });
|
|
|
|
const sayid = table.findOneWhere({ name: "Sayid" });
|
|
|
|
expect(sayid).toEqual({ user_id: 3, name: "Sayid" });
|
|
});
|
|
|
|
test(".findOneWhereOrFail() throws if it finds nothing", () => {
|
|
const table = makeTestTable();
|
|
|
|
expect(() => {
|
|
table.findOneWhereOrFail({ name: "Sayid" });
|
|
}).toThrow();
|
|
});
|
|
|
|
test(".findAllWhere() returns a row based on basic criteria", () => {
|
|
const table = makeTestTable();
|
|
table.insertPartial({ name: "Jack" });
|
|
table.insertPartial({ name: "Jack" });
|
|
table.insertPartial({ name: "Jack" });
|
|
|
|
const manyJacks = table.findAllWhere({ name: "Jack" });
|
|
|
|
expect(manyJacks).toHaveLength(3);
|
|
expect(manyJacks[0]).toEqual({ user_id: 1, name: "Jack" });
|
|
expect(manyJacks[1]).toEqual({ user_id: 2, name: "Jack" });
|
|
expect(manyJacks[2]).toEqual({ user_id: 3, name: "Jack" });
|
|
});
|
|
|
|
test(".findOneById() returns a single element by its primary column", () => {
|
|
const table = makeTestTable();
|
|
|
|
table.insertPartial({ name: "Jack" });
|
|
table.insertPartial({ name: "Kate" });
|
|
table.insertPartial({ name: "Sayid" });
|
|
|
|
const sayid = table.findOneById(3);
|
|
expect(sayid).toEqual({ user_id: 3, name: "Sayid" });
|
|
});
|
|
|
|
test(".findOneById() returns null when nothing was found", () => {
|
|
const table = makeTestTable();
|
|
const nobody = table.findOneById(3);
|
|
expect(nobody).toBeNull();
|
|
});
|
|
|
|
test(".findOneByIdOrFail() returns a single element by its primary column", () => {
|
|
const table = makeTestTable();
|
|
|
|
table.insertPartial({ name: "Jack" });
|
|
table.insertPartial({ name: "Kate" });
|
|
table.insertPartial({ name: "Sayid" });
|
|
|
|
const sayid = table.findOneById(3);
|
|
expect(sayid).toEqual({ user_id: 3, name: "Sayid" });
|
|
});
|
|
|
|
test(".findOneByIdOrFail() throws an error when nothing is found", () => {
|
|
const table = makeTestTable();
|
|
expect(() => {
|
|
table.findOneByIdOrFail(3);
|
|
}).toThrow();
|
|
});
|
|
|
|
test(".delete() deletes a full record", () => {
|
|
const table = makeTestTable();
|
|
table.insertPartial({ name: "Jack" });
|
|
table.insertPartial({ name: "Kate" });
|
|
table.insertPartial({ name: "Sayid" });
|
|
|
|
expect(table.size()).toBe(3);
|
|
|
|
table.delete(table.findOneByIdOrFail(2));
|
|
|
|
expect(table.size()).toBe(2);
|
|
|
|
expect(table.findAll()).toEqual([
|
|
{ user_id: 1, name: "Jack" },
|
|
{ user_id: 3, name: "Sayid" },
|
|
]);
|
|
});
|
|
|
|
test(".deleteById() deletes the element with the given primary value", () => {
|
|
const table = makeTestTable();
|
|
table.insertPartial({ name: "Jack" });
|
|
table.insertPartial({ name: "Kate" });
|
|
table.insertPartial({ name: "Sayid" });
|
|
|
|
expect(table.size()).toBe(3);
|
|
|
|
table.deleteById(2);
|
|
|
|
expect(table.size()).toBe(2);
|
|
|
|
expect(table.findAll()).toEqual([
|
|
{ user_id: 1, name: "Jack" },
|
|
{ user_id: 3, name: "Sayid" },
|
|
]);
|
|
});
|
|
|
|
test(".deleteWhere() deletes all rows that match", () => {
|
|
const table = makeTestTable();
|
|
table.insertPartial({ name: "Jack" });
|
|
table.insertPartial({ name: "Jack" });
|
|
table.insertPartial({ name: "Sayid" });
|
|
|
|
expect(table.size()).toBe(3);
|
|
|
|
table.deleteWhere({ name: "Jack" });
|
|
|
|
expect(table.size()).toBe(1);
|
|
|
|
expect(table.findAll()).toEqual([{ user_id: 3, name: "Sayid" }]);
|
|
});
|
|
|
|
test(".deleteById() respects when references are set to cascade", () => {
|
|
const pq = new Prequel();
|
|
const users = pq.table<UserWithID>("Users", {
|
|
user_id: {
|
|
type: "INTEGER",
|
|
primary: true,
|
|
},
|
|
name: "TEXT",
|
|
});
|
|
|
|
const posts = pq.table<Post>("Posts", {
|
|
post_id: {
|
|
type: "INTEGER",
|
|
primary: true,
|
|
},
|
|
user_id: {
|
|
type: "INTEGER",
|
|
references: users.reference(),
|
|
cascade: true,
|
|
},
|
|
});
|
|
|
|
const user = users.insertPartial({ name: "Bob" });
|
|
posts.insertPartial({
|
|
user_id: user.user_id,
|
|
});
|
|
|
|
users.deleteById(user.user_id);
|
|
expect(posts.size()).toBe(0);
|
|
});
|
|
|
|
test(".deleteAll() deletes all rows in the table", () => {
|
|
const table = makeTestTable();
|
|
table.insertPartial({ name: "Jack" });
|
|
table.insertPartial({ name: "Kate" });
|
|
table.insertPartial({ name: "Sayid" });
|
|
|
|
expect(table.count()).toBe(3);
|
|
|
|
table.deleteAll();
|
|
|
|
expect(table.count()).toBe(0);
|
|
});
|
|
|
|
test(".update() updates a full record", () => {
|
|
const table = makeTestTable();
|
|
const locke = table.insertPartial({ name: "Locke" });
|
|
table.insertPartial({ name: "Kate" });
|
|
table.insertPartial({ name: "Sayid" });
|
|
|
|
locke.name = "Jeremy Bentham";
|
|
table.update(locke);
|
|
|
|
const newLocke = table.findOneById(1);
|
|
expect(newLocke?.name).toEqual("Jeremy Bentham");
|
|
});
|
|
|
|
test(".update() throws if no primary column is set besides ROWID", () => {
|
|
const db = new Database();
|
|
const table = new Table<UserWithID>(db, "Users", {
|
|
user_id: "INTEGER",
|
|
name: "TEXT",
|
|
});
|
|
|
|
const user = table.insert({ user_id: 1, name: "User" });
|
|
|
|
const oops = () => {
|
|
table.update(user);
|
|
};
|
|
|
|
expect(oops).toThrow(/primary column/);
|
|
});
|
|
|
|
test(".updateWhere() updates rows based on the condition", () => {
|
|
const db = new Database();
|
|
const table = new Table<UserWithID>(db, "Users", {
|
|
user_id: "INTEGER",
|
|
name: "TEXT",
|
|
});
|
|
|
|
table.insert({ user_id: 1, name: "Jack" });
|
|
table.insert({ user_id: 2, name: "Kate" });
|
|
table.insert({ user_id: 3, name: "Sayid" });
|
|
|
|
table.updateWhere({ name: "Sawyer" }, { user_id: 2 });
|
|
expect(table.findOneById(2)?.name).toEqual("Sawyer");
|
|
});
|
|
|
|
test(".updateAll() updates all rows in the table", () => {
|
|
const db = new Database();
|
|
const table = new Table<UserWithID>(db, "Users", {
|
|
user_id: "INTEGER",
|
|
name: "TEXT",
|
|
});
|
|
|
|
table.insert({ user_id: 1, name: "Jack" });
|
|
table.insert({ user_id: 2, name: "Kate" });
|
|
table.insert({ user_id: 3, name: "Sayid" });
|
|
|
|
table.updateAll({ name: "Sawyer" });
|
|
expect(table.findAll().map((u) => u.name)).toEqual([
|
|
"Sawyer",
|
|
"Sawyer",
|
|
"Sawyer",
|
|
]);
|
|
});
|