Skip to content

Commit fa9d4e6

Browse files
committed
Refactor query-related types to enhance clarity and consistency. Update BaseQueryBuilder and SelectQueryBuilder to use QueryResult and QueryRowsPayload, respectively, improving type accuracy in SQL execution and pagination options.
1 parent b878910 commit fa9d4e6

File tree

15 files changed

+1293
-9
lines changed

15 files changed

+1293
-9
lines changed

__tests__/repository/count.test.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { Repository } from "../../src/repository/repository";
2+
import { eq, gt, and, like } from "../../src";
3+
import { executor, setupTestTables, cleanupTestData } from "../setup";
4+
5+
interface User {
6+
id: number;
7+
name: string;
8+
email: string;
9+
age?: number;
10+
}
11+
12+
describe("Repository count", () => {
13+
let repository: Repository<User>;
14+
15+
beforeAll(async () => {
16+
await setupTestTables();
17+
});
18+
19+
beforeEach(async () => {
20+
repository = new Repository("users", executor);
21+
await cleanupTestData();
22+
});
23+
24+
it("should count all rows without condition", async () => {
25+
// Insert test data
26+
await executor.executeSQL(
27+
`INSERT INTO users (name, email, age) VALUES ($1, $2, $3), ($4, $5, $6) RETURNING *`,
28+
["John Doe", "[email protected]", 30, "Jane Doe", "[email protected]", 25]
29+
);
30+
31+
const result = await repository.count(eq("id", 1));
32+
33+
expect(result).toBe(1);
34+
});
35+
36+
it("should count rows with condition", async () => {
37+
// Insert test data
38+
await executor.executeSQL(
39+
`INSERT INTO users (name, email, age) VALUES ($1, $2, $3), ($4, $5, $6) RETURNING *`,
40+
["John Doe", "[email protected]", 30, "Jane Doe", "[email protected]", 25]
41+
);
42+
43+
const result = await repository.count(gt("age", 25));
44+
console.log({result})
45+
46+
expect(result).toBe(1);
47+
});
48+
49+
it("should return 0 when no rows match condition", async () => {
50+
// Insert test data
51+
await executor.executeSQL(
52+
`INSERT INTO users (name, email, age) VALUES ($1, $2, $3), ($4, $5, $6) RETURNING *`,
53+
["John Doe", "[email protected]", 30, "Jane Doe", "[email protected]", 25]
54+
);
55+
56+
const result = await repository.count(gt("age", 100));
57+
58+
expect(result).toBe(0);
59+
});
60+
61+
it("should handle complex where conditions", async () => {
62+
// Insert test data
63+
await executor.executeSQL(
64+
`INSERT INTO users (name, email, age) VALUES ($1, $2, $3), ($4, $5, $6) RETURNING *`,
65+
["John Doe", "[email protected]", 30, "Jane Doe", "[email protected]", 25]
66+
);
67+
68+
const result = await repository.count(
69+
and(gt("age", 25), like("name", "%Doe%"))
70+
);
71+
72+
expect(result).toBe(1);
73+
});
74+
75+
it("should handle errors during execution", async () => {
76+
// Try to count with an invalid column name
77+
await expect(
78+
repository.count(eq("invalid_column" as any, 1))
79+
).rejects.toThrow();
80+
});
81+
});
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { SqlExecutor, QueryResult } from "../../src";
2+
import { Repository } from "../../src/repository/repository";
3+
import { eq, gt, and, like } from "../../src";
4+
5+
// Mock SqlExecutor
6+
const mockExecutor: jest.Mocked<SqlExecutor> = {
7+
executeSQL: jest.fn(),
8+
};
9+
10+
interface User {
11+
id: string;
12+
name: string;
13+
email: string;
14+
age?: number;
15+
}
16+
17+
describe("Repository delete", () => {
18+
let repository: Repository<User>;
19+
20+
beforeEach(() => {
21+
repository = new Repository("users", mockExecutor);
22+
jest.clearAllMocks();
23+
});
24+
25+
it("should delete a record and return it", async () => {
26+
const mockUser: User = {
27+
id: "1",
28+
name: "John Doe",
29+
30+
age: 30,
31+
};
32+
33+
const mockResult: QueryResult = {
34+
rows: [mockUser],
35+
};
36+
37+
mockExecutor.executeSQL.mockResolvedValueOnce(mockResult);
38+
39+
const result = await repository.delete(eq("id", "1"));
40+
41+
expect(mockExecutor.executeSQL).toHaveBeenCalledTimes(1);
42+
expect(result).toEqual(mockUser);
43+
});
44+
45+
it("should delete a record with specific returning columns", async () => {
46+
const mockUser: Partial<User> = {
47+
id: "1",
48+
name: "John Doe",
49+
};
50+
51+
const mockResult: QueryResult = {
52+
rows: [mockUser],
53+
};
54+
55+
mockExecutor.executeSQL.mockResolvedValueOnce(mockResult);
56+
57+
const result = await repository.delete(eq("id", "1"), ["id", "name"]);
58+
59+
expect(mockExecutor.executeSQL).toHaveBeenCalledTimes(1);
60+
expect(result).toEqual(mockUser);
61+
});
62+
63+
it("should return null when no record is deleted", async () => {
64+
const mockResult: QueryResult = {
65+
rows: [],
66+
};
67+
68+
mockExecutor.executeSQL.mockResolvedValueOnce(mockResult);
69+
70+
const result = await repository.delete(eq("id", "999"));
71+
72+
expect(mockExecutor.executeSQL).toHaveBeenCalledTimes(1);
73+
expect(result).toBeNull();
74+
});
75+
76+
it("should handle complex where conditions", async () => {
77+
const mockUser: User = {
78+
id: "1",
79+
name: "John Doe",
80+
81+
age: 30,
82+
};
83+
84+
const mockResult: QueryResult = {
85+
rows: [mockUser],
86+
};
87+
88+
mockExecutor.executeSQL.mockResolvedValueOnce(mockResult);
89+
90+
const result = await repository.delete(
91+
and(gt("age", 25), like("name", "%Doe%"))
92+
);
93+
94+
expect(mockExecutor.executeSQL).toHaveBeenCalledTimes(1);
95+
expect(result).toEqual(mockUser);
96+
});
97+
98+
it("should handle errors during execution", async () => {
99+
const error = new Error("Database error");
100+
mockExecutor.executeSQL.mockRejectedValueOnce(error);
101+
102+
await expect(repository.delete(eq("id", "1"))).rejects.toThrow(
103+
"Database error"
104+
);
105+
});
106+
});
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { Repository } from "../../src/repository/repository";
2+
import { eq, and, gt, like } from "../../src";
3+
import { executor, setupTestTables, cleanupTestData } from "../setup";
4+
5+
interface User {
6+
id: number;
7+
name: string;
8+
email: string;
9+
age?: number;
10+
}
11+
12+
describe("Repository findRow", () => {
13+
let repository: Repository<User>;
14+
15+
beforeAll(async () => {
16+
await setupTestTables();
17+
});
18+
19+
beforeEach(async () => {
20+
repository = new Repository("users", executor);
21+
await cleanupTestData();
22+
});
23+
24+
it("should find a single row by condition", async () => {
25+
// Insert test data
26+
await executor.executeSQL(
27+
`INSERT INTO users (name, email, age) VALUES ($1, $2, $3)`,
28+
["John Doe", "[email protected]", 30]
29+
);
30+
31+
const result = await repository.findRow(eq("email", "[email protected]"));
32+
33+
expect(result).toMatchObject({
34+
name: "John Doe",
35+
36+
age: 30,
37+
});
38+
});
39+
40+
it("should return null when no row is found", async () => {
41+
const result = await repository.findRow(eq("id", 999));
42+
43+
expect(result).toBeNull();
44+
});
45+
46+
it("should handle complex where conditions", async () => {
47+
// Insert test data
48+
await executor.executeSQL(
49+
`INSERT INTO users (name, email, age) VALUES ($1, $2, $3) RETURNING *`,
50+
["John Doe", "[email protected]", 30]
51+
);
52+
53+
const result = await repository.findRow(
54+
and(eq("name", "John Doe"), gt("age", 25))
55+
);
56+
57+
expect(result).toEqual({
58+
id: 1,
59+
name: "John Doe",
60+
61+
age: 30,
62+
});
63+
});
64+
65+
it("should handle errors during execution", async () => {
66+
// Try to find a row with an invalid column name
67+
await expect(
68+
repository.findRow(eq("invalid_column" as any, "1"))
69+
).rejects.toThrow();
70+
});
71+
});
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { Repository } from "../../src/repository/repository";
2+
import { OrderBy } from "../../src/types/query";
3+
import { gt, and, like } from "../../src/operators";
4+
import { executor, setupTestTables, cleanupTestData } from "../setup";
5+
6+
interface User {
7+
id: number;
8+
name: string;
9+
email: string;
10+
age?: number;
11+
}
12+
13+
describe("Repository findRows", () => {
14+
let repository: Repository<User>;
15+
16+
beforeAll(async () => {
17+
await setupTestTables();
18+
});
19+
20+
beforeEach(async () => {
21+
repository = new Repository("users", executor);
22+
await cleanupTestData();
23+
});
24+
25+
it("should find multiple rows with simple condition", async () => {
26+
// Insert test data
27+
await executor.executeSQL(
28+
`INSERT INTO users (name, email, age) VALUES ($1, $2, $3), ($4, $5, $6) RETURNING *`,
29+
["John Doe", "[email protected]", 30, "Jane Doe", "[email protected]", 25]
30+
);
31+
32+
const result = await repository.findRows({ where: gt("age", 20) });
33+
34+
expect(result).toHaveLength(2);
35+
expect(result).toEqual([
36+
{
37+
id: 1,
38+
name: "John Doe",
39+
40+
age: 30,
41+
},
42+
{
43+
id: 2,
44+
name: "Jane Doe",
45+
46+
age: 25,
47+
},
48+
]);
49+
});
50+
51+
it("should find rows with ordering", async () => {
52+
// Insert test data
53+
await executor.executeSQL(
54+
`INSERT INTO users (name, email, age) VALUES ($1, $2, $3), ($4, $5, $6) RETURNING *`,
55+
["John Doe", "[email protected]", 30, "Jane Doe", "[email protected]", 25]
56+
);
57+
58+
const orderBy: OrderBy<User>[] = [
59+
{
60+
key: "age",
61+
direction: "desc",
62+
},
63+
];
64+
65+
const result = await repository.findRows({ orderBy });
66+
67+
expect(result).toHaveLength(2);
68+
expect(result[0].age).toBe(30);
69+
expect(result[1].age).toBe(25);
70+
});
71+
72+
it("should find rows with limit and offset", async () => {
73+
// Insert test data
74+
await executor.executeSQL(
75+
`INSERT INTO users (name, email, age) VALUES ($1, $2, $3), ($4, $5, $6) RETURNING *`,
76+
["John Doe", "[email protected]", 30, "Jane Doe", "[email protected]", 25]
77+
);
78+
79+
const result = await repository.findRows({
80+
limit: 1,
81+
offset: 1,
82+
});
83+
84+
expect(result).toHaveLength(1);
85+
expect(result[0].name).toBe("Jane Doe");
86+
});
87+
88+
it("should return empty array when no rows are found", async () => {
89+
const result = await repository.findRows();
90+
91+
expect(result).toEqual([]);
92+
});
93+
94+
it("should handle errors during execution", async () => {
95+
// Try to find rows with an invalid column name
96+
await expect(
97+
repository.findRows({ where: gt("invalid_column" as any, 20) })
98+
).rejects.toThrow();
99+
});
100+
});

0 commit comments

Comments
 (0)