Add biome
This commit is contained in:
parent
09d25c926c
commit
6479d09196
9 changed files with 586 additions and 538 deletions
34
biome.json
Normal file
34
biome.json
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
||||||
|
"vcs": {
|
||||||
|
"enabled": false,
|
||||||
|
"clientKind": "git",
|
||||||
|
"useIgnoreFile": true
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"ignoreUnknown": false,
|
||||||
|
"ignore": ["dist"]
|
||||||
|
},
|
||||||
|
"formatter": {
|
||||||
|
"enabled": true,
|
||||||
|
"indentStyle": "space",
|
||||||
|
"indentWidth": 2
|
||||||
|
},
|
||||||
|
"organizeImports": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"linter": {
|
||||||
|
"enabled": true,
|
||||||
|
"rules": {
|
||||||
|
"recommended": true,
|
||||||
|
"suspicious": {
|
||||||
|
"noArrayIndexKey": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"javascript": {
|
||||||
|
"formatter": {
|
||||||
|
"quoteStyle": "double"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
10
package.json
10
package.json
|
@ -5,14 +5,13 @@
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "bun run ./build.ts",
|
"build": "bun run ./build.ts",
|
||||||
"clean": "rm -rf dist"
|
"clean": "rm -rf dist",
|
||||||
|
"fmt": "biome check --fix"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Endeavorance <hello@endeavorance.camp> (https://endeavorance.camp)",
|
"author": "Endeavorance <hello@endeavorance.camp> (https://endeavorance.camp)",
|
||||||
"license": "CC BY-NC-SA 4.0",
|
"license": "CC BY-NC-SA 4.0",
|
||||||
"files": [
|
"files": ["dist"],
|
||||||
"dist"
|
|
||||||
],
|
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
|
@ -20,5 +19,8 @@
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5.0.0"
|
"typescript": "^5.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@biomejs/biome": "^1.9.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ export const ColumnOf = {
|
||||||
type: otherTable.primaryColumnType(),
|
type: otherTable.primaryColumnType(),
|
||||||
references: otherTable.reference(),
|
references: otherTable.reference(),
|
||||||
nullable: false,
|
nullable: false,
|
||||||
cascade
|
cascade,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -36,7 +36,6 @@ export const ColumnOf = {
|
||||||
Text: {
|
Text: {
|
||||||
type: "TEXT",
|
type: "TEXT",
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Int: {
|
Int: {
|
||||||
|
@ -94,7 +93,7 @@ export const ColumnOf = {
|
||||||
return {
|
return {
|
||||||
type: "TEXT",
|
type: "TEXT",
|
||||||
default: defaultValue,
|
default: defaultValue,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
Int(defaultValue: number): ColumnShorthand {
|
Int(defaultValue: number): ColumnShorthand {
|
||||||
|
@ -109,7 +108,6 @@ export const ColumnOf = {
|
||||||
type: "REAL",
|
type: "REAL",
|
||||||
default: defaultValue,
|
default: defaultValue,
|
||||||
} as const;
|
} as const;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
11
src/index.ts
11
src/index.ts
|
@ -1,6 +1,13 @@
|
||||||
|
import { ColumnOf } from "./column-types";
|
||||||
import type { Column, ColumnShorthand, DataType } from "./columns";
|
import type { Column, ColumnShorthand, DataType } from "./columns";
|
||||||
import { Prequel } from "./prequel";
|
import { Prequel } from "./prequel";
|
||||||
import { Table } from "./table";
|
import { Table } from "./table";
|
||||||
import { ColumnOf } from "./column-types";
|
|
||||||
|
|
||||||
export { Prequel, Table, ColumnOf, type DataType, type Column, type ColumnShorthand };
|
export {
|
||||||
|
Prequel,
|
||||||
|
Table,
|
||||||
|
ColumnOf,
|
||||||
|
type DataType,
|
||||||
|
type Column,
|
||||||
|
type ColumnShorthand,
|
||||||
|
};
|
||||||
|
|
322
src/prequel.ts
322
src/prequel.ts
|
@ -4,194 +4,194 @@ import { SchemaError } from "./error";
|
||||||
import { Table } from "./table";
|
import { Table } from "./table";
|
||||||
|
|
||||||
interface KVRow {
|
interface KVRow {
|
||||||
kv_id: number;
|
kv_id: number;
|
||||||
key: string;
|
key: string;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a key-value store using Prequel.
|
* Represents a key-value store using Prequel.
|
||||||
*/
|
*/
|
||||||
export class PrequelKVStore {
|
export class PrequelKVStore {
|
||||||
private table: Table<KVRow>;
|
private table: Table<KVRow>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of the PrequelKVStore class.
|
* Creates a new instance of the PrequelKVStore class.
|
||||||
* @param pq The Prequel instance.
|
* @param pq The Prequel instance.
|
||||||
* @param tableName The name of the table.
|
* @param tableName The name of the table.
|
||||||
*/
|
*/
|
||||||
constructor(pq: Prequel, tableName: string) {
|
constructor(pq: Prequel, tableName: string) {
|
||||||
this.table = pq.table<KVRow>(tableName, {
|
this.table = pq.table<KVRow>(tableName, {
|
||||||
kv_id: {
|
kv_id: {
|
||||||
type: "INTEGER",
|
type: "INTEGER",
|
||||||
primary: true,
|
primary: true,
|
||||||
autoincrement: true,
|
autoincrement: true,
|
||||||
},
|
},
|
||||||
key: "TEXT",
|
key: "TEXT",
|
||||||
value: "TEXT",
|
value: "TEXT",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts or updates a key-value pair in the store.
|
* Inserts or updates a key-value pair in the store.
|
||||||
* @param key The key.
|
* @param key The key.
|
||||||
* @param value The value.
|
* @param value The value.
|
||||||
*/
|
*/
|
||||||
put<T>(key: string, value: T): void {
|
put<T>(key: string, value: T): void {
|
||||||
const lookup = this.table.findOneWhere({
|
const lookup = this.table.findOneWhere({
|
||||||
key,
|
key,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (lookup === null) {
|
if (lookup === null) {
|
||||||
this.table.insertPartial({
|
this.table.insertPartial({
|
||||||
key,
|
key,
|
||||||
value: JSON.stringify(value),
|
value: JSON.stringify(value),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
lookup.value = JSON.stringify(value);
|
lookup.value = JSON.stringify(value);
|
||||||
this.table.update(lookup);
|
this.table.update(lookup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the value associated with the specified key from the store.
|
* Retrieves the value associated with the specified key from the store.
|
||||||
* @param key The key.
|
* @param key The key.
|
||||||
* @returns The value associated with the key, or null if the key is not found.
|
* @returns The value associated with the key, or null if the key is not found.
|
||||||
*/
|
*/
|
||||||
get<T>(key: string): T | null {
|
get<T>(key: string): T | null {
|
||||||
const lookup = this.table.findOneWhere({ key });
|
const lookup = this.table.findOneWhere({ key });
|
||||||
|
|
||||||
if (lookup === null) {
|
if (lookup === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return JSON.parse(lookup.value) as T;
|
return JSON.parse(lookup.value) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a key exists in the table.
|
* Checks if a key exists in the table.
|
||||||
* @param key - The key to check.
|
* @param key - The key to check.
|
||||||
* @returns `true` if the key exists, `false` otherwise.
|
* @returns `true` if the key exists, `false` otherwise.
|
||||||
*/
|
*/
|
||||||
has(key: string): boolean {
|
has(key: string): boolean {
|
||||||
return (
|
return (
|
||||||
this.table.findOneWhere({
|
this.table.findOneWhere({
|
||||||
key,
|
key,
|
||||||
}) !== null
|
}) !== null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an array of all of the keys in the KV store
|
* Get an array of all of the keys in the KV store
|
||||||
* @returns The keys in the store.
|
* @returns The keys in the store.
|
||||||
*/
|
*/
|
||||||
keys(): string[] {
|
keys(): string[] {
|
||||||
return this.table.findAll().map((row) => row.key);
|
return this.table.findAll().map((row) => row.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an array of all of the values in the KV store
|
* Get an array of all of the values in the KV store
|
||||||
* @returns The values in the store.
|
* @returns The values in the store.
|
||||||
*/
|
*/
|
||||||
values(): unknown[] {
|
values(): unknown[] {
|
||||||
return this.table.findAll().map((row) => JSON.parse(row.value));
|
return this.table.findAll().map((row) => JSON.parse(row.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a Prequel database instance.
|
* Represents a Prequel database instance.
|
||||||
*/
|
*/
|
||||||
export class Prequel {
|
export class Prequel {
|
||||||
public db: Database;
|
public db: Database;
|
||||||
public kv: PrequelKVStore;
|
public kv: PrequelKVStore;
|
||||||
public tables: Record<string, Table<unknown>> = {};
|
public tables: Record<string, Table<unknown>> = {};
|
||||||
public query: typeof Database.prototype.query;
|
public query: typeof Database.prototype.query;
|
||||||
public uncachedQuery: typeof Database.prototype.prepare;
|
public uncachedQuery: typeof Database.prototype.prepare;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Prequel database instance.
|
* Creates a new Prequel database instance.
|
||||||
* @param filename The filename of the database. Defaults to ":memory:" if not provided.
|
* @param filename The filename of the database. Defaults to ":memory:" if not provided.
|
||||||
*/
|
*/
|
||||||
constructor(filename = ":memory:") {
|
constructor(filename = ":memory:") {
|
||||||
this.db = new Database(filename);
|
this.db = new Database(filename);
|
||||||
this.query = this.db.query.bind(this.db);
|
this.query = this.db.query.bind(this.db);
|
||||||
this.uncachedQuery = this.db.prepare.bind(this.db);
|
this.uncachedQuery = this.db.prepare.bind(this.db);
|
||||||
this.db.exec("PRAGMA foreign_keys=ON");
|
this.db.exec("PRAGMA foreign_keys=ON");
|
||||||
this.kv = new PrequelKVStore(this, "PrequelManagedKVStore");
|
this.kv = new PrequelKVStore(this, "PrequelManagedKVStore");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables Write-Ahead Logging (WAL) mode for the database.
|
* Enables Write-Ahead Logging (WAL) mode for the database.
|
||||||
*/
|
*/
|
||||||
enableWAL() {
|
enableWAL() {
|
||||||
this.db.exec("PRAGMA journal_mode=WAL;");
|
this.db.exec("PRAGMA journal_mode=WAL;");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the database connection.
|
* Closes the database connection.
|
||||||
*/
|
*/
|
||||||
close() {
|
close() {
|
||||||
this.db.close();
|
this.db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a table with the given name exists in the database.
|
* Checks if a table with the given name exists in the database.
|
||||||
* @param name The name of the table.
|
* @param name The name of the table.
|
||||||
* @returns `true` if the table exists, `false` otherwise.
|
* @returns `true` if the table exists, `false` otherwise.
|
||||||
*/
|
*/
|
||||||
hasTable(name: string): boolean {
|
hasTable(name: string): boolean {
|
||||||
return Object.keys(this.tables).includes(name);
|
return Object.keys(this.tables).includes(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new table in the database.
|
* Creates a new table in the database.
|
||||||
* @param name The name of the table.
|
* @param name The name of the table.
|
||||||
* @param cols The column definitions of the table.
|
* @param cols The column definitions of the table.
|
||||||
* @returns The created table.
|
* @returns The created table.
|
||||||
* @throws {SchemaError} If a table with the same name already exists.
|
* @throws {SchemaError} If a table with the same name already exists.
|
||||||
*/
|
*/
|
||||||
table<RowShape>(
|
table<RowShape>(
|
||||||
name: string,
|
name: string,
|
||||||
cols: ColumnShorthandMap<RowShape>,
|
cols: ColumnShorthandMap<RowShape>,
|
||||||
): Table<RowShape> {
|
): Table<RowShape> {
|
||||||
if (this.hasTable(name)) {
|
if (this.hasTable(name)) {
|
||||||
throw new SchemaError("Duplicate table name", name);
|
throw new SchemaError("Duplicate table name", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newTable = new Table(this, name, cols);
|
const newTable = new Table(this, name, cols);
|
||||||
this.tables[name] = newTable;
|
this.tables[name] = newTable;
|
||||||
return newTable;
|
return newTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attaches an existing table to the database.
|
* Attaches an existing table to the database.
|
||||||
* @param table The table to attach.
|
* @param table The table to attach.
|
||||||
* @throws {SchemaError} If a table with the same name already exists.
|
* @throws {SchemaError} If a table with the same name already exists.
|
||||||
*/
|
*/
|
||||||
attachTable(table: Table<unknown>) {
|
attachTable(table: Table<unknown>) {
|
||||||
if (this.hasTable(table.name)) {
|
if (this.hasTable(table.name)) {
|
||||||
throw new SchemaError("Duplicate table name", table.name);
|
throw new SchemaError("Duplicate table name", table.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tables[table.name] = table;
|
this.tables[table.name] = table;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Executes a function within a database transaction.
|
* Executes a function within a database transaction.
|
||||||
* If the function throws an error, the transaction is rolled back.
|
* If the function throws an error, the transaction is rolled back.
|
||||||
* Otherwise, the transaction is committed.
|
* Otherwise, the transaction is committed.
|
||||||
* @param transactionFn The function to execute within the transaction.
|
* @param transactionFn The function to execute within the transaction.
|
||||||
* @returns `true` if the transaction was committed successfully, otherwise `false`.
|
* @returns `true` if the transaction was committed successfully, otherwise `false`.
|
||||||
* @throws The error thrown by the transaction function if the transaction fails.
|
* @throws The error thrown by the transaction function if the transaction fails.
|
||||||
*/
|
*/
|
||||||
transaction<T>(transactionFn: () => T): T {
|
transaction<T>(transactionFn: () => T): T {
|
||||||
try {
|
try {
|
||||||
this.db.exec("BEGIN TRANSACTION;");
|
this.db.exec("BEGIN TRANSACTION;");
|
||||||
const result: T = transactionFn();
|
const result: T = transactionFn();
|
||||||
this.db.exec("COMMIT;");
|
this.db.exec("COMMIT;");
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.db.exec("ROLLBACK;");
|
this.db.exec("ROLLBACK;");
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
15
src/table.ts
15
src/table.ts
|
@ -37,9 +37,15 @@ function shapeForQuery<T>(obj: Partial<T>): ArbitraryRow {
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
const val = obj[key as keyof T];
|
const val = obj[key as keyof T];
|
||||||
|
|
||||||
if (typeof val !== "string" && typeof val !== "number" && val !== null) {
|
if (
|
||||||
|
typeof val === "string" &&
|
||||||
|
typeof val === "number" &&
|
||||||
|
typeof val === "boolean" &&
|
||||||
|
typeof val === "bigint" &&
|
||||||
|
val !== null
|
||||||
|
) {
|
||||||
throw new SchemaError(
|
throw new SchemaError(
|
||||||
`Invalid value in query: ${key}: ${val} (type: ${typeof val})`,
|
`Invalid value in query: ${key} -> ${val} (type: ${typeof val})`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +103,7 @@ export class Table<RowShape> {
|
||||||
for (const columnName of this._columnNames) {
|
for (const columnName of this._columnNames) {
|
||||||
const columnDefinition =
|
const columnDefinition =
|
||||||
this._columnDefinitions[
|
this._columnDefinitions[
|
||||||
columnName as keyof typeof this._columnDefinitions
|
columnName as keyof typeof this._columnDefinitions
|
||||||
];
|
];
|
||||||
|
|
||||||
// Identify a primary column besides ROWID if specified
|
// Identify a primary column besides ROWID if specified
|
||||||
|
@ -160,7 +166,8 @@ export class Table<RowShape> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public primaryColumnType(): DataType {
|
public primaryColumnType(): DataType {
|
||||||
const primary = this._primaryColumnName as keyof typeof this._columnDefinitions;
|
const primary = this
|
||||||
|
._primaryColumnName as keyof typeof this._columnDefinitions;
|
||||||
return this._columnDefinitions[primary].type;
|
return this._columnDefinitions[primary].type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,143 +3,143 @@ import { SchemaError } from "../src/error";
|
||||||
import { Prequel, Table } from "../src/index";
|
import { Prequel, Table } from "../src/index";
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
user_id: number;
|
user_id: number;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
test("constructor", () => {
|
test("constructor", () => {
|
||||||
const pq = new Prequel();
|
const pq = new Prequel();
|
||||||
const users = pq.table<User>("Users", {
|
const users = pq.table<User>("Users", {
|
||||||
user_id: {
|
user_id: {
|
||||||
type: "INTEGER",
|
type: "INTEGER",
|
||||||
primary: true,
|
primary: true,
|
||||||
},
|
},
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
});
|
});
|
||||||
|
|
||||||
users.insertPartial({ name: "Yondu" });
|
users.insertPartial({ name: "Yondu" });
|
||||||
|
|
||||||
expect(users.findAll().length).toBe(1);
|
expect(users.findAll().length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".hasTable() knows if a table exists in the DB", () => {
|
test(".hasTable() knows if a table exists in the DB", () => {
|
||||||
const pq = new Prequel();
|
const pq = new Prequel();
|
||||||
|
|
||||||
expect(pq.hasTable("Users")).toBeFalse();
|
expect(pq.hasTable("Users")).toBeFalse();
|
||||||
expect(pq.hasTable("Notfound")).toBeFalse();
|
expect(pq.hasTable("Notfound")).toBeFalse();
|
||||||
|
|
||||||
pq.table<User>("Users", {
|
pq.table<User>("Users", {
|
||||||
user_id: {
|
user_id: {
|
||||||
type: "INTEGER",
|
type: "INTEGER",
|
||||||
primary: true,
|
primary: true,
|
||||||
},
|
},
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(pq.hasTable("Users")).toBeTrue();
|
expect(pq.hasTable("Users")).toBeTrue();
|
||||||
expect(pq.hasTable("Notfound")).toBeFalse();
|
expect(pq.hasTable("Notfound")).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".table() creates and attaches a table", () => {
|
test(".table() creates and attaches a table", () => {
|
||||||
const pq = new Prequel();
|
const pq = new Prequel();
|
||||||
|
|
||||||
// Already has one table because of KV
|
// Already has one table because of KV
|
||||||
expect(Object.keys(pq.tables)).toHaveLength(1);
|
expect(Object.keys(pq.tables)).toHaveLength(1);
|
||||||
|
|
||||||
const table = pq.table<User>("Users", {
|
const table = pq.table<User>("Users", {
|
||||||
user_id: {
|
user_id: {
|
||||||
type: "INTEGER",
|
type: "INTEGER",
|
||||||
primary: true,
|
primary: true,
|
||||||
},
|
},
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(Object.keys(pq.tables)).toHaveLength(2);
|
expect(Object.keys(pq.tables)).toHaveLength(2);
|
||||||
expect(Object.values(pq.tables)).toContain(table);
|
expect(Object.values(pq.tables)).toContain(table);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".table() throws when creating a duplicate table", () => {
|
test(".table() throws when creating a duplicate table", () => {
|
||||||
const pq = new Prequel();
|
const pq = new Prequel();
|
||||||
pq.table<User>("Users", {
|
pq.table<User>("Users", {
|
||||||
user_id: {
|
user_id: {
|
||||||
type: "INTEGER",
|
type: "INTEGER",
|
||||||
primary: true,
|
primary: true,
|
||||||
},
|
},
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
});
|
});
|
||||||
|
|
||||||
const oopsie = () => {
|
const oopsie = () => {
|
||||||
pq.table<User>("Users", {
|
pq.table<User>("Users", {
|
||||||
user_id: {
|
user_id: {
|
||||||
type: "INTEGER",
|
type: "INTEGER",
|
||||||
primary: true,
|
primary: true,
|
||||||
},
|
},
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(oopsie).toThrow(SchemaError);
|
expect(oopsie).toThrow(SchemaError);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".attachTable() attaches a table", () => {
|
test(".attachTable() attaches a table", () => {
|
||||||
const pq = new Prequel();
|
const pq = new Prequel();
|
||||||
const table = new Table<User>(pq.db, "Users", {
|
const table = new Table<User>(pq.db, "Users", {
|
||||||
user_id: {
|
user_id: {
|
||||||
type: "INTEGER",
|
type: "INTEGER",
|
||||||
primary: true,
|
primary: true,
|
||||||
},
|
},
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(pq.hasTable("Users")).toBeFalse();
|
expect(pq.hasTable("Users")).toBeFalse();
|
||||||
|
|
||||||
pq.attachTable(table);
|
pq.attachTable(table);
|
||||||
|
|
||||||
expect(pq.hasTable("Users")).toBeTrue();
|
expect(pq.hasTable("Users")).toBeTrue();
|
||||||
expect(Object.values(pq.tables)).toContain(table);
|
expect(Object.values(pq.tables)).toContain(table);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".attachTable() throws on duplicate table names", () => {
|
test(".attachTable() throws on duplicate table names", () => {
|
||||||
const pq = new Prequel();
|
const pq = new Prequel();
|
||||||
pq.table<User>("Users", {
|
pq.table<User>("Users", {
|
||||||
user_id: {
|
user_id: {
|
||||||
type: "INTEGER",
|
type: "INTEGER",
|
||||||
primary: true,
|
primary: true,
|
||||||
},
|
},
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
});
|
});
|
||||||
|
|
||||||
const secondTable = new Table<User>(pq.db, "Users", {
|
const secondTable = new Table<User>(pq.db, "Users", {
|
||||||
user_id: {
|
user_id: {
|
||||||
type: "INTEGER",
|
type: "INTEGER",
|
||||||
primary: true,
|
primary: true,
|
||||||
},
|
},
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
});
|
});
|
||||||
|
|
||||||
const doAttachment = () => {
|
const doAttachment = () => {
|
||||||
pq.attachTable(secondTable);
|
pq.attachTable(secondTable);
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(doAttachment).toThrow(SchemaError);
|
expect(doAttachment).toThrow(SchemaError);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("kv store can set and retrieve arbitrary values", () => {
|
test("kv store can set and retrieve arbitrary values", () => {
|
||||||
const pq = new Prequel();
|
const pq = new Prequel();
|
||||||
|
|
||||||
pq.kv.put<number>("answer", 42);
|
pq.kv.put<number>("answer", 42);
|
||||||
pq.kv.put<{ enabled: boolean }>("config", { enabled: true });
|
pq.kv.put<{ enabled: boolean }>("config", { enabled: true });
|
||||||
pq.kv.put<string[]>("list", ["a", "b", "c"]);
|
pq.kv.put<string[]>("list", ["a", "b", "c"]);
|
||||||
|
|
||||||
expect(pq.kv.get<number>("answer")).toEqual(42);
|
expect(pq.kv.get<number>("answer")).toEqual(42);
|
||||||
expect(pq.kv.get<{ enabled: boolean }>("config")).toEqual({ enabled: true });
|
expect(pq.kv.get<{ enabled: boolean }>("config")).toEqual({ enabled: true });
|
||||||
expect(pq.kv.get<string[]>("list")).toEqual(["a", "b", "c"]);
|
expect(pq.kv.get<string[]>("list")).toEqual(["a", "b", "c"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".query() creates a cached statement", () => {
|
test(".query() creates a cached statement", () => {
|
||||||
const pq = new Prequel();
|
const pq = new Prequel();
|
||||||
const query = pq.query("SELECT 1");
|
const query = pq.query("SELECT 1");
|
||||||
|
|
||||||
expect(query).toBeDefined();
|
expect(query).toBeDefined();
|
||||||
expect(query).toBe(pq.query("SELECT 1"));
|
expect(query).toBe(pq.query("SELECT 1"));
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,414 +3,414 @@ import { expect, test } from "bun:test";
|
||||||
import { Prequel, Table } from "../src/index";
|
import { Prequel, Table } from "../src/index";
|
||||||
|
|
||||||
interface UserWithID {
|
interface UserWithID {
|
||||||
user_id: number;
|
user_id: number;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UserWithBio {
|
interface UserWithBio {
|
||||||
user_id: number;
|
user_id: number;
|
||||||
bio: {
|
bio: {
|
||||||
name: string;
|
name: string;
|
||||||
age: number;
|
age: number;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Post {
|
interface Post {
|
||||||
post_id: number;
|
post_id: number;
|
||||||
user_id: number;
|
user_id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UserWithNullable extends UserWithID {
|
interface UserWithNullable extends UserWithID {
|
||||||
bio: string | null;
|
bio: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeTestTable(): Table<UserWithID> {
|
function makeTestTable(): Table<UserWithID> {
|
||||||
const db = new Database();
|
const db = new Database();
|
||||||
return new Table<UserWithID>(db, "Users", {
|
return new Table<UserWithID>(db, "Users", {
|
||||||
user_id: {
|
user_id: {
|
||||||
type: "INTEGER",
|
type: "INTEGER",
|
||||||
primary: true,
|
primary: true,
|
||||||
},
|
},
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
test("constructor", () => {
|
test("constructor", () => {
|
||||||
const db = new Database();
|
const db = new Database();
|
||||||
const table = new Table<UserWithID>(db, "Users", {
|
const table = new Table<UserWithID>(db, "Users", {
|
||||||
user_id: {
|
user_id: {
|
||||||
type: "INTEGER",
|
type: "INTEGER",
|
||||||
primary: true,
|
primary: true,
|
||||||
},
|
},
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(table.name).toEqual("Users");
|
expect(table.name).toEqual("Users");
|
||||||
expect(table.db).toBe(db);
|
expect(table.db).toBe(db);
|
||||||
expect(table.columns).toEqual(["user_id", "name"]);
|
expect(table.columns).toEqual(["user_id", "name"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("constructor -> auto-attach to a prequel isntance", () => {
|
test("constructor -> auto-attach to a prequel isntance", () => {
|
||||||
const db = new Prequel();
|
const db = new Prequel();
|
||||||
const table = new Table<UserWithID>(db, "Users", {
|
const table = new Table<UserWithID>(db, "Users", {
|
||||||
user_id: {
|
user_id: {
|
||||||
type: "INTEGER",
|
type: "INTEGER",
|
||||||
primary: true,
|
primary: true,
|
||||||
},
|
},
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(Object.values(db.tables)).toContain(table);
|
expect(Object.values(db.tables)).toContain(table);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".toString() resolves to the table name for embedding in queries", () => {
|
test(".toString() resolves to the table name for embedding in queries", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
expect(`${table}`).toEqual('"Users"');
|
expect(`${table}`).toEqual('"Users"');
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".reference() creates a default reference to the primary column", () => {
|
test(".reference() creates a default reference to the primary column", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
expect(table.reference()).toEqual("Users(user_id)");
|
expect(table.reference()).toEqual("Users(user_id)");
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".reference() creates a reference to the specified column", () => {
|
test(".reference() creates a reference to the specified column", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
expect(table.reference("name")).toEqual("Users(name)");
|
expect(table.reference("name")).toEqual("Users(name)");
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".insert() inserts a full row", () => {
|
test(".insert() inserts a full row", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
table.insert({
|
table.insert({
|
||||||
user_id: 100,
|
user_id: 100,
|
||||||
name: "Jack",
|
name: "Jack",
|
||||||
});
|
});
|
||||||
|
|
||||||
const lookup = table.db.prepare<UserWithID, number>(
|
const lookup = table.db.prepare<UserWithID, number>(
|
||||||
`SELECT * FROM ${table} WHERE user_id = ?`,
|
`SELECT * FROM ${table} WHERE user_id = ?`,
|
||||||
);
|
);
|
||||||
const found = lookup.get(100);
|
const found = lookup.get(100);
|
||||||
expect(found).toEqual({
|
expect(found).toEqual({
|
||||||
user_id: 100,
|
user_id: 100,
|
||||||
name: "Jack",
|
name: "Jack",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".insert() is able to insert nulls to nullable fields", () => {
|
test(".insert() is able to insert nulls to nullable fields", () => {
|
||||||
const table = new Table<UserWithNullable>(new Database(), "Users", {
|
const table = new Table<UserWithNullable>(new Database(), "Users", {
|
||||||
user_id: {
|
user_id: {
|
||||||
type: "INTEGER",
|
type: "INTEGER",
|
||||||
primary: true,
|
primary: true,
|
||||||
},
|
},
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
bio: {
|
bio: {
|
||||||
type: "TEXT",
|
type: "TEXT",
|
||||||
nullable: true,
|
nullable: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
table.insert({
|
table.insert({
|
||||||
user_id: 1,
|
user_id: 1,
|
||||||
name: "Jack",
|
name: "Jack",
|
||||||
bio: null,
|
bio: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const lookup = table.db.prepare<UserWithNullable, number>(
|
const lookup = table.db.prepare<UserWithNullable, number>(
|
||||||
`SELECT * FROM ${table} WHERE user_id = ?`,
|
`SELECT * FROM ${table} WHERE user_id = ?`,
|
||||||
);
|
);
|
||||||
const found = lookup.get(1);
|
const found = lookup.get(1);
|
||||||
|
|
||||||
expect(found?.bio).toBeNull();
|
expect(found?.bio).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".insertPartial() inserts a partial row", () => {
|
test(".insertPartial() inserts a partial row", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
table.insertPartial({
|
table.insertPartial({
|
||||||
name: "Jack",
|
name: "Jack",
|
||||||
});
|
});
|
||||||
|
|
||||||
const lookup = table.db.prepare<UserWithID, string>(
|
const lookup = table.db.prepare<UserWithID, string>(
|
||||||
`SELECT * FROM ${table} WHERE name = ?`,
|
`SELECT * FROM ${table} WHERE name = ?`,
|
||||||
);
|
);
|
||||||
const found = lookup.get("Jack");
|
const found = lookup.get("Jack");
|
||||||
expect(found).toEqual({
|
expect(found).toEqual({
|
||||||
user_id: 1,
|
user_id: 1,
|
||||||
name: "Jack",
|
name: "Jack",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".insertWithout() inserts a partial row with type safety", () => {
|
test(".insertWithout() inserts a partial row with type safety", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
table.insertWithout<"user_id">({
|
table.insertWithout<"user_id">({
|
||||||
name: "Jack",
|
name: "Jack",
|
||||||
});
|
});
|
||||||
|
|
||||||
const lookup = table.db.prepare<UserWithID, string>(
|
const lookup = table.db.prepare<UserWithID, string>(
|
||||||
`SELECT * FROM ${table} WHERE name = ?`,
|
`SELECT * FROM ${table} WHERE name = ?`,
|
||||||
);
|
);
|
||||||
const found = lookup.get("Jack");
|
const found = lookup.get("Jack");
|
||||||
expect(found).toEqual({
|
expect(found).toEqual({
|
||||||
user_id: 1,
|
user_id: 1,
|
||||||
name: "Jack",
|
name: "Jack",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".count() gets the current number of rows in the table", () => {
|
test(".count() gets the current number of rows in the table", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
table.insertPartial({ name: "Jack" });
|
table.insertPartial({ name: "Jack" });
|
||||||
table.insertPartial({ name: "Kate" });
|
table.insertPartial({ name: "Kate" });
|
||||||
table.insertPartial({ name: "Sayid" });
|
table.insertPartial({ name: "Sayid" });
|
||||||
expect(table.count()).toBe(3);
|
expect(table.count()).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".countWhere() gets the current number of rows that match a condition", () => {
|
test(".countWhere() gets the current number of rows that match a condition", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
table.insertPartial({ name: "Jack" });
|
table.insertPartial({ name: "Jack" });
|
||||||
table.insertPartial({ name: "Kate" });
|
table.insertPartial({ name: "Kate" });
|
||||||
table.insertPartial({ name: "Sayid" });
|
table.insertPartial({ name: "Sayid" });
|
||||||
expect(table.countWhere({ name: "Jack" })).toBe(1);
|
expect(table.countWhere({ name: "Jack" })).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".findAll() returns the full table", () => {
|
test(".findAll() returns the full table", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
table.insertPartial({ name: "Jack" });
|
table.insertPartial({ name: "Jack" });
|
||||||
table.insertPartial({ name: "Kate" });
|
table.insertPartial({ name: "Kate" });
|
||||||
table.insertPartial({ name: "Sayid" });
|
table.insertPartial({ name: "Sayid" });
|
||||||
const allUsers = table.findAll();
|
const allUsers = table.findAll();
|
||||||
|
|
||||||
expect(allUsers).toHaveLength(3);
|
expect(allUsers).toHaveLength(3);
|
||||||
expect(allUsers[0]).toEqual({ user_id: 1, name: "Jack" });
|
expect(allUsers[0]).toEqual({ user_id: 1, name: "Jack" });
|
||||||
expect(allUsers[1]).toEqual({ user_id: 2, name: "Kate" });
|
expect(allUsers[1]).toEqual({ user_id: 2, name: "Kate" });
|
||||||
expect(allUsers[2]).toEqual({ user_id: 3, name: "Sayid" });
|
expect(allUsers[2]).toEqual({ user_id: 3, name: "Sayid" });
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".query() returns a prepared statement for the table", () => {
|
test(".query() returns a prepared statement for the table", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
const query = table.query("SELECT * FROM Users WHERE name = ?");
|
const query = table.query("SELECT * FROM Users WHERE name = ?");
|
||||||
expect(query).toBeDefined();
|
expect(query).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".findOneWhere() returns a row based on basic criteria", () => {
|
test(".findOneWhere() returns a row based on basic criteria", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
table.insertPartial({ name: "Jack" });
|
table.insertPartial({ name: "Jack" });
|
||||||
table.insertPartial({ name: "Kate" });
|
table.insertPartial({ name: "Kate" });
|
||||||
table.insertPartial({ name: "Sayid" });
|
table.insertPartial({ name: "Sayid" });
|
||||||
|
|
||||||
const sayid = table.findOneWhere({ name: "Sayid" });
|
const sayid = table.findOneWhere({ name: "Sayid" });
|
||||||
|
|
||||||
expect(sayid).toEqual({ user_id: 3, name: "Sayid" });
|
expect(sayid).toEqual({ user_id: 3, name: "Sayid" });
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".findOneWhereOrFail() throws if it finds nothing", () => {
|
test(".findOneWhereOrFail() throws if it finds nothing", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
table.findOneWhereOrFail({ name: "Sayid" });
|
table.findOneWhereOrFail({ name: "Sayid" });
|
||||||
}).toThrow();
|
}).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".findAllWhere() returns a row based on basic criteria", () => {
|
test(".findAllWhere() returns a row based on basic criteria", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
table.insertPartial({ name: "Jack" });
|
table.insertPartial({ name: "Jack" });
|
||||||
table.insertPartial({ name: "Jack" });
|
table.insertPartial({ name: "Jack" });
|
||||||
table.insertPartial({ name: "Jack" });
|
table.insertPartial({ name: "Jack" });
|
||||||
|
|
||||||
const manyJacks = table.findAllWhere({ name: "Jack" });
|
const manyJacks = table.findAllWhere({ name: "Jack" });
|
||||||
|
|
||||||
expect(manyJacks).toHaveLength(3);
|
expect(manyJacks).toHaveLength(3);
|
||||||
expect(manyJacks[0]).toEqual({ user_id: 1, name: "Jack" });
|
expect(manyJacks[0]).toEqual({ user_id: 1, name: "Jack" });
|
||||||
expect(manyJacks[1]).toEqual({ user_id: 2, name: "Jack" });
|
expect(manyJacks[1]).toEqual({ user_id: 2, name: "Jack" });
|
||||||
expect(manyJacks[2]).toEqual({ user_id: 3, name: "Jack" });
|
expect(manyJacks[2]).toEqual({ user_id: 3, name: "Jack" });
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".findOneById() returns a single element by its primary column", () => {
|
test(".findOneById() returns a single element by its primary column", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
|
|
||||||
table.insertPartial({ name: "Jack" });
|
table.insertPartial({ name: "Jack" });
|
||||||
table.insertPartial({ name: "Kate" });
|
table.insertPartial({ name: "Kate" });
|
||||||
table.insertPartial({ name: "Sayid" });
|
table.insertPartial({ name: "Sayid" });
|
||||||
|
|
||||||
const sayid = table.findOneById(3);
|
const sayid = table.findOneById(3);
|
||||||
expect(sayid).toEqual({ user_id: 3, name: "Sayid" });
|
expect(sayid).toEqual({ user_id: 3, name: "Sayid" });
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".findOneById() returns null when nothing was found", () => {
|
test(".findOneById() returns null when nothing was found", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
const nobody = table.findOneById(3);
|
const nobody = table.findOneById(3);
|
||||||
expect(nobody).toBeNull();
|
expect(nobody).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".findOneByIdOrFail() returns a single element by its primary column", () => {
|
test(".findOneByIdOrFail() returns a single element by its primary column", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
|
|
||||||
table.insertPartial({ name: "Jack" });
|
table.insertPartial({ name: "Jack" });
|
||||||
table.insertPartial({ name: "Kate" });
|
table.insertPartial({ name: "Kate" });
|
||||||
table.insertPartial({ name: "Sayid" });
|
table.insertPartial({ name: "Sayid" });
|
||||||
|
|
||||||
const sayid = table.findOneById(3);
|
const sayid = table.findOneById(3);
|
||||||
expect(sayid).toEqual({ user_id: 3, name: "Sayid" });
|
expect(sayid).toEqual({ user_id: 3, name: "Sayid" });
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".findOneByIdOrFail() throws an error when nothing is found", () => {
|
test(".findOneByIdOrFail() throws an error when nothing is found", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
expect(() => {
|
expect(() => {
|
||||||
table.findOneByIdOrFail(3);
|
table.findOneByIdOrFail(3);
|
||||||
}).toThrow();
|
}).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".delete() deletes a full record", () => {
|
test(".delete() deletes a full record", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
table.insertPartial({ name: "Jack" });
|
table.insertPartial({ name: "Jack" });
|
||||||
table.insertPartial({ name: "Kate" });
|
table.insertPartial({ name: "Kate" });
|
||||||
table.insertPartial({ name: "Sayid" });
|
table.insertPartial({ name: "Sayid" });
|
||||||
|
|
||||||
expect(table.size()).toBe(3);
|
expect(table.size()).toBe(3);
|
||||||
|
|
||||||
table.delete(table.findOneByIdOrFail(2));
|
table.delete(table.findOneByIdOrFail(2));
|
||||||
|
|
||||||
expect(table.size()).toBe(2);
|
expect(table.size()).toBe(2);
|
||||||
|
|
||||||
expect(table.findAll()).toEqual([
|
expect(table.findAll()).toEqual([
|
||||||
{ user_id: 1, name: "Jack" },
|
{ user_id: 1, name: "Jack" },
|
||||||
{ user_id: 3, name: "Sayid" },
|
{ user_id: 3, name: "Sayid" },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".deleteById() deletes the element with the given primary value", () => {
|
test(".deleteById() deletes the element with the given primary value", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
table.insertPartial({ name: "Jack" });
|
table.insertPartial({ name: "Jack" });
|
||||||
table.insertPartial({ name: "Kate" });
|
table.insertPartial({ name: "Kate" });
|
||||||
table.insertPartial({ name: "Sayid" });
|
table.insertPartial({ name: "Sayid" });
|
||||||
|
|
||||||
expect(table.size()).toBe(3);
|
expect(table.size()).toBe(3);
|
||||||
|
|
||||||
table.deleteById(2);
|
table.deleteById(2);
|
||||||
|
|
||||||
expect(table.size()).toBe(2);
|
expect(table.size()).toBe(2);
|
||||||
|
|
||||||
expect(table.findAll()).toEqual([
|
expect(table.findAll()).toEqual([
|
||||||
{ user_id: 1, name: "Jack" },
|
{ user_id: 1, name: "Jack" },
|
||||||
{ user_id: 3, name: "Sayid" },
|
{ user_id: 3, name: "Sayid" },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".deleteWhere() deletes all rows that match", () => {
|
test(".deleteWhere() deletes all rows that match", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
table.insertPartial({ name: "Jack" });
|
table.insertPartial({ name: "Jack" });
|
||||||
table.insertPartial({ name: "Jack" });
|
table.insertPartial({ name: "Jack" });
|
||||||
table.insertPartial({ name: "Sayid" });
|
table.insertPartial({ name: "Sayid" });
|
||||||
|
|
||||||
expect(table.size()).toBe(3);
|
expect(table.size()).toBe(3);
|
||||||
|
|
||||||
table.deleteWhere({ name: "Jack" });
|
table.deleteWhere({ name: "Jack" });
|
||||||
|
|
||||||
expect(table.size()).toBe(1);
|
expect(table.size()).toBe(1);
|
||||||
|
|
||||||
expect(table.findAll()).toEqual([{ user_id: 3, name: "Sayid" }]);
|
expect(table.findAll()).toEqual([{ user_id: 3, name: "Sayid" }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".deleteById() respects when references are set to cascade", () => {
|
test(".deleteById() respects when references are set to cascade", () => {
|
||||||
const pq = new Prequel();
|
const pq = new Prequel();
|
||||||
const users = pq.table<UserWithID>("Users", {
|
const users = pq.table<UserWithID>("Users", {
|
||||||
user_id: {
|
user_id: {
|
||||||
type: "INTEGER",
|
type: "INTEGER",
|
||||||
primary: true,
|
primary: true,
|
||||||
},
|
},
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
});
|
});
|
||||||
|
|
||||||
const posts = pq.table<Post>("Posts", {
|
const posts = pq.table<Post>("Posts", {
|
||||||
post_id: {
|
post_id: {
|
||||||
type: "INTEGER",
|
type: "INTEGER",
|
||||||
primary: true,
|
primary: true,
|
||||||
},
|
},
|
||||||
user_id: {
|
user_id: {
|
||||||
type: "INTEGER",
|
type: "INTEGER",
|
||||||
references: users.reference(),
|
references: users.reference(),
|
||||||
cascade: true,
|
cascade: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const user = users.insertPartial({ name: "Bob" });
|
const user = users.insertPartial({ name: "Bob" });
|
||||||
posts.insertPartial({
|
posts.insertPartial({
|
||||||
user_id: user.user_id,
|
user_id: user.user_id,
|
||||||
});
|
});
|
||||||
|
|
||||||
users.deleteById(user.user_id);
|
users.deleteById(user.user_id);
|
||||||
expect(posts.size()).toBe(0);
|
expect(posts.size()).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".deleteAll() deletes all rows in the table", () => {
|
test(".deleteAll() deletes all rows in the table", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
table.insertPartial({ name: "Jack" });
|
table.insertPartial({ name: "Jack" });
|
||||||
table.insertPartial({ name: "Kate" });
|
table.insertPartial({ name: "Kate" });
|
||||||
table.insertPartial({ name: "Sayid" });
|
table.insertPartial({ name: "Sayid" });
|
||||||
|
|
||||||
expect(table.count()).toBe(3);
|
expect(table.count()).toBe(3);
|
||||||
|
|
||||||
table.deleteAll();
|
table.deleteAll();
|
||||||
|
|
||||||
expect(table.count()).toBe(0);
|
expect(table.count()).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".update() updates a full record", () => {
|
test(".update() updates a full record", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
const locke = table.insertPartial({ name: "Locke" });
|
const locke = table.insertPartial({ name: "Locke" });
|
||||||
table.insertPartial({ name: "Kate" });
|
table.insertPartial({ name: "Kate" });
|
||||||
table.insertPartial({ name: "Sayid" });
|
table.insertPartial({ name: "Sayid" });
|
||||||
|
|
||||||
locke.name = "Jeremy Bentham";
|
locke.name = "Jeremy Bentham";
|
||||||
table.update(locke);
|
table.update(locke);
|
||||||
|
|
||||||
const newLocke = table.findOneById(1);
|
const newLocke = table.findOneById(1);
|
||||||
expect(newLocke?.name).toEqual("Jeremy Bentham");
|
expect(newLocke?.name).toEqual("Jeremy Bentham");
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".update() throws if no primary column is set besides ROWID", () => {
|
test(".update() throws if no primary column is set besides ROWID", () => {
|
||||||
const db = new Database();
|
const db = new Database();
|
||||||
const table = new Table<UserWithID>(db, "Users", {
|
const table = new Table<UserWithID>(db, "Users", {
|
||||||
user_id: "INTEGER",
|
user_id: "INTEGER",
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
});
|
});
|
||||||
|
|
||||||
const user = table.insert({ user_id: 1, name: "User" });
|
const user = table.insert({ user_id: 1, name: "User" });
|
||||||
|
|
||||||
const oops = () => {
|
const oops = () => {
|
||||||
table.update(user);
|
table.update(user);
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(oops).toThrow(/primary column/);
|
expect(oops).toThrow(/primary column/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".updateWhere() updates rows based on the condition", () => {
|
test(".updateWhere() updates rows based on the condition", () => {
|
||||||
const db = new Database();
|
const db = new Database();
|
||||||
const table = new Table<UserWithID>(db, "Users", {
|
const table = new Table<UserWithID>(db, "Users", {
|
||||||
user_id: "INTEGER",
|
user_id: "INTEGER",
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
});
|
});
|
||||||
|
|
||||||
table.insert({ user_id: 1, name: "Jack" });
|
table.insert({ user_id: 1, name: "Jack" });
|
||||||
table.insert({ user_id: 2, name: "Kate" });
|
table.insert({ user_id: 2, name: "Kate" });
|
||||||
table.insert({ user_id: 3, name: "Sayid" });
|
table.insert({ user_id: 3, name: "Sayid" });
|
||||||
|
|
||||||
table.updateWhere({ name: "Sawyer" }, { user_id: 2 });
|
table.updateWhere({ name: "Sawyer" }, { user_id: 2 });
|
||||||
expect(table.findOneById(2)?.name).toEqual("Sawyer");
|
expect(table.findOneById(2)?.name).toEqual("Sawyer");
|
||||||
});
|
});
|
||||||
|
|
||||||
test(".updateAll() updates all rows in the table", () => {
|
test(".updateAll() updates all rows in the table", () => {
|
||||||
const db = new Database();
|
const db = new Database();
|
||||||
const table = new Table<UserWithID>(db, "Users", {
|
const table = new Table<UserWithID>(db, "Users", {
|
||||||
user_id: "INTEGER",
|
user_id: "INTEGER",
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
});
|
});
|
||||||
|
|
||||||
table.insert({ user_id: 1, name: "Jack" });
|
table.insert({ user_id: 1, name: "Jack" });
|
||||||
table.insert({ user_id: 2, name: "Kate" });
|
table.insert({ user_id: 2, name: "Kate" });
|
||||||
table.insert({ user_id: 3, name: "Sayid" });
|
table.insert({ user_id: 3, name: "Sayid" });
|
||||||
|
|
||||||
table.updateAll({ name: "Sawyer" });
|
table.updateAll({ name: "Sawyer" });
|
||||||
expect(table.findAll().map((u) => u.name)).toEqual([
|
expect(table.findAll().map((u) => u.name)).toEqual([
|
||||||
"Sawyer",
|
"Sawyer",
|
||||||
"Sawyer",
|
"Sawyer",
|
||||||
"Sawyer",
|
"Sawyer",
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue