@simplysm/orm-common 13.0.70 → 13.0.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/orm-common",
3
- "version": "13.0.70",
3
+ "version": "13.0.71",
4
4
  "description": "Simplysm Package - ORM Module (common)",
5
5
  "author": "simplysm",
6
6
  "license": "Apache-2.0",
@@ -19,6 +19,6 @@
19
19
  ],
20
20
  "sideEffects": false,
21
21
  "dependencies": {
22
- "@simplysm/core-common": "13.0.70"
22
+ "@simplysm/core-common": "13.0.71"
23
23
  }
24
24
  }
@@ -1,7 +1,7 @@
1
1
  import { mysql, pgsql, tsql } from "@simplysm/core-common";
2
2
  import type { ExpectedSql } from "../setup/test-utils";
3
3
 
4
- //#region ========== 데이터 타입 Basic 테스트 ==========
4
+ //#region ========== Data Type Basic Tests ==========
5
5
 
6
6
  export const intType: ExpectedSql = {
7
7
  mysql: mysql`
@@ -215,7 +215,7 @@ export const uuidType: ExpectedSql = {
215
215
 
216
216
  //#endregion
217
217
 
218
- //#region ========== 메서드 조합 테스트 ==========
218
+ //#region ========== Method Combination Tests ==========
219
219
 
220
220
  export const nullableColumn: ExpectedSql = {
221
221
  mysql: mysql`
@@ -147,7 +147,7 @@ export const updateWithTop: ExpectedSql = {
147
147
 
148
148
  //#endregion
149
149
 
150
- //#region ========== FK 스위치 ==========
150
+ //#region ========== FK Switch ==========
151
151
 
152
152
  export const fkOff: ExpectedSql = {
153
153
  mysql: mysql`
@@ -160,7 +160,7 @@ describe("UPDATE - Basic", () => {
160
160
  .employee()
161
161
  .where((e) => [expr.eq(e.id, 1)])
162
162
  .getUpdateQueryDef((e) => ({
163
- // managerId = managerId + 1 (예시)
163
+ // managerId = managerId + 1 (example)
164
164
  managerId: expr.raw("number")`${e.managerId} + 1`,
165
165
  }));
166
166
 
@@ -19,7 +19,7 @@ describe("Queryable error cases", () => {
19
19
  it("Error when passing parameters to procedure without parameters", () => {
20
20
  const db = createTestDb();
21
21
  expect(() => {
22
- // @ts-expect-error - 파라미터 없는 프로시저에 파라미터 전달 테스트
22
+ // @ts-expect-error - test passing parameters to a procedure that expects none
23
23
  db.getAllUsers().getExecProcQueryDef({ unexpectedParam: 1 });
24
24
  }).toThrow("has no parameters.");
25
25
  });
@@ -50,13 +50,13 @@ describe("MysqlExprRenderer.escapeString", () => {
50
50
  expect(result).toBe("''; DROP TABLE users; --");
51
51
  });
52
52
 
53
- it("백슬래시와 따옴표 조합을 방어해야 ", () => {
53
+ it("should defend against backslash + quote combination", () => {
54
54
  const malicious = "\\'";
55
55
  const result = renderer.escapeString(malicious);
56
56
  expect(result).toBe("\\\\''");
57
57
  });
58
58
 
59
- it("NULL 바이트와 SQL 주석 조합을 방어해야 함", () => {
59
+ it("should defend against NULL byte + SQL comment combination", () => {
60
60
  const malicious = "admin\0-- ";
61
61
  const result = renderer.escapeString(malicious);
62
62
  expect(result).toBe("admin\\0-- ");
@@ -68,32 +68,32 @@ describe("MysqlExprRenderer.escapeString", () => {
68
68
  describe("MysqlExprRenderer.escapeValue", () => {
69
69
  const renderer = new MysqlExprRenderer(() => "");
70
70
 
71
- it("문자열을 escapeString()으로 이스케이프하고 따옴표로 감싸야 ", () => {
71
+ it("escapes string with escapeString() and wraps in quotes", () => {
72
72
  const result = renderer.escapeValue("O'Reilly");
73
73
  expect(result).toBe("'O''Reilly'");
74
74
  });
75
75
 
76
- it("백슬래시가 포함된 문자열을 올바르게 이스케이프해야 함", () => {
76
+ it("correctly escapes string containing backslashes", () => {
77
77
  const result = renderer.escapeValue("C:\\path");
78
78
  expect(result).toBe("'C:\\\\path'");
79
79
  });
80
80
 
81
- it("SQL 인젝션 시도를 방어해야 ", () => {
81
+ it("defends against SQL injection attempts", () => {
82
82
  const result = renderer.escapeValue("'; DROP TABLE users; --");
83
83
  expect(result).toBe("'''; DROP TABLE users; --'");
84
84
  });
85
85
 
86
- it("NULL을 'NULL' 문자열로 반환해야 ", () => {
86
+ it("returns 'NULL' string for null", () => {
87
87
  const result = renderer.escapeValue(null);
88
88
  expect(result).toBe("NULL");
89
89
  });
90
90
 
91
- it("Number를 문자열로 conversion해야 ", () => {
91
+ it("converts number to string", () => {
92
92
  const result = renderer.escapeValue(123);
93
93
  expect(result).toBe("123");
94
94
  });
95
95
 
96
- it("불리언을 TRUE/FALSE로 conversion해야 함", () => {
96
+ it("converts boolean to TRUE/FALSE", () => {
97
97
  expect(renderer.escapeValue(true)).toBe("TRUE");
98
98
  expect(renderer.escapeValue(false)).toBe("FALSE");
99
99
  });
@@ -1,7 +1,7 @@
1
1
  import { mysql, pgsql, tsql } from "@simplysm/core-common";
2
2
  import type { ExpectedSql } from "../setup/test-utils";
3
3
 
4
- //#region ========== PIVOT - Basic 집계 함수 ==========
4
+ //#region ========== PIVOT - Basic Aggregate Functions ==========
5
5
 
6
6
  export const pivotSum: ExpectedSql = {
7
7
  mysql: mysql`
@@ -125,7 +125,7 @@ GROUP BY "T1"."id", "T1"."category"
125
125
 
126
126
  //#endregion
127
127
 
128
- //#region ========== PIVOT - 다중 피벗 ==========
128
+ //#region ========== PIVOT - Multiple Pivot Values ==========
129
129
 
130
130
  export const pivotMultipleYears: ExpectedSql = {
131
131
  mysql: mysql`
@@ -1,7 +1,7 @@
1
1
  import { mysql, pgsql, tsql } from "@simplysm/core-common";
2
2
  import type { ExpectedSql } from "../setup/test-utils";
3
3
 
4
- //#region ========== 랜덤 샘플링 ==========
4
+ //#region ========== Random Sampling ==========
5
5
 
6
6
  export const samplingBasic: ExpectedSql = {
7
7
  mysql: mysql`
@@ -1,9 +1,10 @@
1
1
  /**
2
- * 랜덤 샘플링 예제
2
+ * Random sampling example
3
3
  *
4
- * sample() 메서드 대신 expr.random() orderBy/top을 조합하여 랜덤 샘플링 구현
4
+ * Implements random sampling using expr.random() combined with orderBy/top
5
+ * instead of a dedicated sample() method.
5
6
  *
6
- * 사용법: db.user().orderBy(() => expr.random()).top(5)
7
+ * Usage: db.user().orderBy(() => expr.random()).top(5)
7
8
  */
8
9
  import { describe, expect, it } from "vitest";
9
10
  import { createTestDb } from "../setup/TestDbContext";
@@ -1,7 +1,7 @@
1
1
  import { mysql, pgsql, tsql, DateTime, DateOnly, Time, Uuid } from "@simplysm/core-common";
2
2
  import type { ExpectedSql } from "../setup/test-utils";
3
3
 
4
- // DateTime 테스트용
4
+ // For DateTime value tests
5
5
  export const testDateTime = new DateTime(2024, 1, 15, 10, 30, 0);
6
6
  export const testDateOnly = new DateOnly(2024, 1, 15);
7
7
  export const testTime = new Time(10, 30, 0);
@@ -237,7 +237,7 @@ export const inEmpty: ExpectedSql = {
237
237
  `,
238
238
  };
239
239
 
240
- //#region ========== DateTime/DateOnly/Time 테스트 ==========
240
+ //#region ========== DateTime/DateOnly/Time Value Tests ==========
241
241
 
242
242
  export const eqDateTime: ExpectedSql = {
243
243
  mysql: mysql`
@@ -259,7 +259,7 @@ export const eqDateTime: ExpectedSql = {
259
259
 
260
260
  //#endregion
261
261
 
262
- //#region ========== inQuery 테스트 ==========
262
+ //#region ========== inQuery Tests ==========
263
263
 
264
264
  export const inQuery: ExpectedSql = {
265
265
  mysql: mysql`
@@ -340,7 +340,7 @@ describe("Expr - Comparison operators (null-safe)", () => {
340
340
  });
341
341
  });
342
342
 
343
- //#region ========== DateTime 테스트 ==========
343
+ //#region ========== DateTime value tests ==========
344
344
 
345
345
  describe("eq - DateTime value comparison", () => {
346
346
  const db = createTestDb();
@@ -358,7 +358,7 @@ describe("Expr - Comparison operators (null-safe)", () => {
358
358
 
359
359
  //#endregion
360
360
 
361
- //#region ========== inQuery 테스트 ==========
361
+ //#region ========== inQuery tests ==========
362
362
 
363
363
  describe("inQuery - subquery IN condition", () => {
364
364
  const db = createTestDb();
@@ -218,7 +218,7 @@ describe("Expr - Conditional functions", () => {
218
218
  });
219
219
 
220
220
  describe("switch - all case/default are undefined", () => {
221
- it("에러를 발생시킨다", () => {
221
+ it("throws an error", () => {
222
222
  const db = createTestDb();
223
223
  expect(() => {
224
224
  db.user()
@@ -234,7 +234,7 @@ describe("Expr - Conditional functions", () => {
234
234
  });
235
235
 
236
236
  describe("if - both then and else are undefined", () => {
237
- it("에러를 발생시킨다", () => {
237
+ it("throws an error", () => {
238
238
  const db = createTestDb();
239
239
  expect(() => {
240
240
  db.user()
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Expr - 유틸리티 함수 test expected SQL
2
+ * Expr - utility function test expected SQL
3
3
  */
4
4
  import { mysql, pgsql, tsql } from "@simplysm/core-common";
5
5
  import type { ExpectedSql } from "../setup/test-utils";
@@ -203,7 +203,7 @@ export const selectGreatest: ExpectedSql = {
203
203
 
204
204
  //#endregion
205
205
 
206
- //#region ========== 옵션 ==========
206
+ //#region ========== Options ==========
207
207
 
208
208
  export const selectDistinct: ExpectedSql = {
209
209
  mysql: mysql`
@@ -256,7 +256,7 @@ export const selectDistinctLock: ExpectedSql = {
256
256
 
257
257
  //#endregion
258
258
 
259
- //#region ========== 제한 ==========
259
+ //#region ========== Limit ==========
260
260
 
261
261
  export const selectTop: ExpectedSql = {
262
262
  mysql: mysql`
@@ -317,6 +317,6 @@ export const selectLimitOffset: ExpectedSql = {
317
317
  `,
318
318
  };
319
319
 
320
- // 랜덤 샘플링은 examples/sampling.spec.ts 참고
320
+ // See examples/sampling.spec.ts for random sampling
321
321
 
322
322
  //#endregion
@@ -4,7 +4,7 @@
4
4
  import { mysql, pgsql, tsql } from "@simplysm/core-common";
5
5
  import type { ExpectedSql } from "../setup/test-utils";
6
6
 
7
- //#region ========== 비교 연산 ==========
7
+ //#region ========== Comparison Operators ==========
8
8
 
9
9
  export const whereEq: ExpectedSql = {
10
10
  mysql: mysql`
@@ -116,7 +116,7 @@ export const whereLte: ExpectedSql = {
116
116
 
117
117
  //#endregion
118
118
 
119
- //#region ========== NULL 체크 ==========
119
+ //#region ========== NULL Check ==========
120
120
 
121
121
  export const whereNull: ExpectedSql = {
122
122
  mysql: mysql`
@@ -236,7 +236,7 @@ export const whereNotLike: ExpectedSql = {
236
236
 
237
237
  //#endregion
238
238
 
239
- //#region ========== 논리 연산 ==========
239
+ //#region ========== Logical Operators ==========
240
240
 
241
241
  export const whereMultipleAnd: ExpectedSql = {
242
242
  mysql: mysql`
@@ -6,7 +6,7 @@ import { dialects } from "../setup/test-utils";
6
6
  import "../setup/test-utils"; // toMatchSql matcher
7
7
  import * as expected from "./filter.expected";
8
8
 
9
- //#region ========== 비교 연산 ==========
9
+ //#region ========== comparison operations ==========
10
10
 
11
11
  describe("SELECT - WHERE - comparison operations", () => {
12
12
  describe("eq (equal)", () => {
@@ -183,7 +183,7 @@ describe("SELECT - WHERE - comparison operations", () => {
183
183
 
184
184
  //#endregion
185
185
 
186
- //#region ========== NULL 체크 ==========
186
+ //#region ========== NULL check ==========
187
187
 
188
188
  describe("SELECT - WHERE - NULL check", () => {
189
189
  describe("null", () => {
@@ -383,10 +383,10 @@ describe("SELECT - WHERE - LIKE", () => {
383
383
 
384
384
  //#endregion
385
385
 
386
- //#region ========== 논리 연산 ==========
386
+ //#region ========== logical operations ==========
387
387
 
388
388
  describe("SELECT - WHERE - logical operations", () => {
389
- describe("다중 조건 (AND)", () => {
389
+ describe("multiple conditions (AND)", () => {
390
390
  const db = createTestDb();
391
391
  const def = db
392
392
  .user()
@@ -495,7 +495,7 @@ describe("SELECT - WHERE - logical operations", () => {
495
495
  });
496
496
  });
497
497
 
498
- it("연속 where (AND 결합)", () => {
498
+ it("chained where (AND combination)", () => {
499
499
  const db = createTestDb();
500
500
  const def = db
501
501
  .user()
@@ -601,7 +601,7 @@ describe("SELECT - WHERE - EXISTS / IN subquery", () => {
601
601
  });
602
602
  });
603
603
 
604
- it("not exists - QueryDef 검증", () => {
604
+ it("not exists - verifies QueryDef", () => {
605
605
  const db = createTestDb();
606
606
  const def = db
607
607
  .user()
@@ -683,7 +683,7 @@ describe("SELECT - WHERE - EXISTS / IN subquery", () => {
683
683
  //#region ========== SEARCH ==========
684
684
 
685
685
  describe("SELECT - SEARCH", () => {
686
- it("단일 검색어 - 단일 column", () => {
686
+ it("single keyword - single column", () => {
687
687
  const db = createTestDb();
688
688
  const def = db
689
689
  .post()
@@ -717,7 +717,7 @@ describe("SELECT - SEARCH", () => {
717
717
  });
718
718
  });
719
719
 
720
- it("다중 검색어 (OR)", () => {
720
+ it("multiple keywords (OR)", () => {
721
721
  const db = createTestDb();
722
722
  const def = db
723
723
  .post()
@@ -769,7 +769,7 @@ describe("SELECT - SEARCH", () => {
769
769
  });
770
770
  });
771
771
 
772
- it("구문 검색 (따옴표)", () => {
772
+ it("phrase search (quotes)", () => {
773
773
  const db = createTestDb();
774
774
  const def = db
775
775
  .post()
@@ -803,7 +803,7 @@ describe("SELECT - SEARCH", () => {
803
803
  });
804
804
  });
805
805
 
806
- it("와일드카드 (*)", () => {
806
+ it("wildcard (*)", () => {
807
807
  const db = createTestDb();
808
808
  const def = db
809
809
  .post()
@@ -837,7 +837,7 @@ describe("SELECT - SEARCH", () => {
837
837
  });
838
838
  });
839
839
 
840
- it("이스케이프 (\\*)", () => {
840
+ it("escape (\\*)", () => {
841
841
  const db = createTestDb();
842
842
  const def = db
843
843
  .post()
@@ -871,7 +871,7 @@ describe("SELECT - SEARCH", () => {
871
871
  });
872
872
  });
873
873
 
874
- it("다중 column 검색 (OR)", () => {
874
+ it("multiple column search (OR)", () => {
875
875
  const db = createTestDb();
876
876
  const def = db
877
877
  .post()
@@ -913,7 +913,7 @@ describe("SELECT - SEARCH", () => {
913
913
  });
914
914
  });
915
915
 
916
- it("제외 검색 (-)", () => {
916
+ it("exclusion search (-)", () => {
917
917
  const db = createTestDb();
918
918
  const def = db
919
919
  .post()
@@ -963,7 +963,7 @@ describe("SELECT - SEARCH", () => {
963
963
  });
964
964
  });
965
965
 
966
- it("복합 검색 (포함, 제외, 구문)", () => {
966
+ it("complex search (include, exclude, phrase)", () => {
967
967
  const db = createTestDb();
968
968
  const def = db
969
969
  .post()
@@ -1050,7 +1050,7 @@ describe("SELECT - SEARCH", () => {
1050
1050
  });
1051
1051
  });
1052
1052
 
1053
- it(" 검색어", () => {
1053
+ it("empty search term", () => {
1054
1054
  const db = createTestDb();
1055
1055
  const def = db
1056
1056
  .post()
@@ -213,7 +213,7 @@ describe("SELECT - JOIN", () => {
213
213
  });
214
214
  });
215
215
 
216
- describe("다단계 join(Single)", () => {
216
+ describe("multi-level joinSingle", () => {
217
217
  const db = createTestDb();
218
218
  const def = db
219
219
  .post()
@@ -563,7 +563,7 @@ describe("SELECT - INCLUDE", () => {
563
563
  .include((item) => item.user.company)
564
564
  .getSelectQueryDef();
565
565
 
566
- // 중복 include는 자동으로 제거됨 (user 1번, company 1번)
566
+ // duplicate includes are automatically deduplicated (user once, company once)
567
567
  expect(def).toEqual({
568
568
  type: "select",
569
569
  as: "T1",
@@ -707,7 +707,7 @@ describe("SELECT - INCLUDE", () => {
707
707
  });
708
708
 
709
709
  describe("3 depth include (FK -> FKT -> FK)", () => {
710
- // Post user(FK) posts(FKT) user(FK)
710
+ // Post -> user(FK) -> posts(FKT) -> user(FK)
711
711
  const db = createTestDb();
712
712
  const def = db
713
713
  .post()
@@ -62,7 +62,7 @@ export const orderDesc: ExpectedSql = {
62
62
 
63
63
  //#endregion
64
64
 
65
- //#region ========== 다중 정렬 ==========
65
+ //#region ========== Multiple Sort ==========
66
66
 
67
67
  export const orderMultiple: ExpectedSql = {
68
68
  mysql: mysql`
@@ -84,7 +84,7 @@ export const orderMultiple: ExpectedSql = {
84
84
 
85
85
  //#endregion
86
86
 
87
- //#region ========== 표현식 정렬 ==========
87
+ //#region ========== Expression Sort ==========
88
88
 
89
89
  export const orderExpression: ExpectedSql = {
90
90
  mysql: mysql`
@@ -106,7 +106,7 @@ export const orderExpression: ExpectedSql = {
106
106
 
107
107
  //#endregion
108
108
 
109
- //#region ========== 조합 ==========
109
+ //#region ========== Combination ==========
110
110
 
111
111
  export const orderSelectCombo: ExpectedSql = {
112
112
  mysql: mysql`
@@ -76,7 +76,7 @@ describe("SELECT - ORDER BY", () => {
76
76
 
77
77
  //#endregion
78
78
 
79
- //#region ========== 다중 정렬 ==========
79
+ //#region ========== multiple sorting ==========
80
80
 
81
81
  describe("multiple sorting", () => {
82
82
  const db = createTestDb();
@@ -106,7 +106,7 @@ describe("SELECT - ORDER BY", () => {
106
106
 
107
107
  //#endregion
108
108
 
109
- //#region ========== 표현식 정렬 ==========
109
+ //#region ========== expression sorting ==========
110
110
 
111
111
  describe("sort by expressions", () => {
112
112
  const db = createTestDb();
@@ -132,7 +132,7 @@ describe("SELECT - ORDER BY", () => {
132
132
 
133
133
  //#endregion
134
134
 
135
- //#region ========== 조합 ==========
135
+ //#region ========== combinations ==========
136
136
 
137
137
  describe("SELECT + ORDER BY combination", () => {
138
138
  const db = createTestDb();
@@ -4,7 +4,7 @@
4
4
  import { mysql, pgsql, tsql } from "@simplysm/core-common";
5
5
  import type { ExpectedSql } from "../setup/test-utils";
6
6
 
7
- //#region ========== Basic 재귀 CTE ==========
7
+ //#region ========== Basic Recursive CTE ==========
8
8
 
9
9
  export const basicSubordinates: ExpectedSql = {
10
10
  mysql: mysql`
@@ -143,7 +143,7 @@ export const upwardManagers: ExpectedSql = {
143
143
 
144
144
  //#endregion
145
145
 
146
- //#region ========== CTE + 후processing ==========
146
+ //#region ========== CTE + Post Processing ==========
147
147
 
148
148
  export const cteWithOrderBy: ExpectedSql = {
149
149
  mysql: mysql`
@@ -1,10 +1,10 @@
1
1
  /**
2
- * SELECT - 서브쿼리/WRAP/UNION test expected SQL
2
+ * SELECT - Subquery/WRAP/UNION test expected SQL
3
3
  */
4
4
  import { mysql, pgsql, tsql } from "@simplysm/core-common";
5
5
  import type { ExpectedSql } from "../setup/test-utils";
6
6
 
7
- //#region ========== WRAP (서브쿼리) ==========
7
+ //#region ========== WRAP (Subquery) ==========
8
8
 
9
9
  export const wrapBasic: ExpectedSql = {
10
10
  mysql: mysql`
@@ -80,7 +80,7 @@ export const viewSelectOrderByLimit: ExpectedSql = {
80
80
 
81
81
  //#endregion
82
82
 
83
- //#region ========== View - SELECT가 있는 ==========
83
+ //#region ========== View - View With SELECT ==========
84
84
 
85
85
  export const userSummarySelect: ExpectedSql = {
86
86
  mysql: mysql`SELECT * FROM \`TestDb\`.\`UserSummary\` AS \`T1\``,
@@ -2,16 +2,16 @@ import type { DbContextExecutor, ResultMeta } from "../../src/types/db";
2
2
  import type { QueryDef } from "../../src/types/query-def";
3
3
 
4
4
  export class MockExecutor implements DbContextExecutor {
5
- // 연결 관리 (Mock - 아무것도 안 함)
5
+ // connection management (Mock - no-op)
6
6
  async connect(): Promise<void> {}
7
7
  async close(): Promise<void> {}
8
8
 
9
- // 트랜잭션 (Mock - 아무것도 안 함)
9
+ // transactions (Mock - no-op)
10
10
  async beginTransaction(): Promise<void> {}
11
11
  async commitTransaction(): Promise<void> {}
12
12
  async rollbackTransaction(): Promise<void> {}
13
13
 
14
- // 쿼리 실행
14
+ // query execution
15
15
  executeDefs(_defs: QueryDef[], _resultMetas?: (ResultMeta | undefined)[]): Promise<any[][]> {
16
16
  return Promise.resolve([[]]);
17
17
  }
@@ -4,7 +4,7 @@ export const Employee = Table("Employee")
4
4
  .columns((c) => ({
5
5
  id: c.bigint().autoIncrement(),
6
6
  name: c.varchar(100),
7
- managerId: c.bigint().nullable(), // 자기 참조 (상위 매니저)
7
+ managerId: c.bigint().nullable(), // self-referencing (parent manager)
8
8
  departmentId: c.bigint().nullable(),
9
9
  }))
10
10
  .primaryKey("id");
@@ -2,7 +2,7 @@ import { View } from "../../../src/schema/view-builder";
2
2
  import { expr } from "../../../src/expr/expr";
3
3
  import type { TestDbTablesContext } from "../TestDbContext";
4
4
 
5
- // 활성 사용자만 보여주는
5
+ // view showing only active users
6
6
  export const ActiveUsers = View("ActiveUsers").query((db: TestDbTablesContext) =>
7
7
  db.user().where((u) => [expr.eq(u.isActive, true)]),
8
8
  );
@@ -1,7 +1,7 @@
1
1
  import { View } from "../../../src/schema/view-builder";
2
2
  import type { TestDbTablesContext } from "../TestDbContext";
3
3
 
4
- // 사용자 요약 정보 (select 포함)
4
+ // view for user summary info (includes select)
5
5
  export const UserSummary = View("UserSummary").query((db: TestDbTablesContext) =>
6
6
  db.user().select((u) => ({
7
7
  id: u.id,
@@ -3,16 +3,16 @@ import { parseQueryResult } from "../../src/utils/result-parser";
3
3
  import type { ResultMeta } from "../../src/types/db";
4
4
 
5
5
  /**
6
- * 성능 벤치마크 테스트
6
+ * Performance benchmark tests
7
7
  *
8
- * 목적: objClone 제거 최적화 효과 측정
9
- * 측정 항목:
10
- * - 대용량 단순 레코드 processing 시간
11
- * - 대용량 JOIN 레코드 processing 시간
12
- * - 중첩 JOIN processing 시간
8
+ * Purpose: measure optimization effect of removing objClone
9
+ * Metrics:
10
+ * - processing time for large simple records
11
+ * - processing time for large JOIN records
12
+ * - processing time for nested JOIN records
13
13
  */
14
14
  describe("result-parser performance", () => {
15
- // 테스트 데이터 생성 헬퍼
15
+ // test data generation helpers
16
16
  function generateSimpleRecords(count: number): Record<string, unknown>[] {
17
17
  return Array.from({ length: count }, (_, i) => ({
18
18
  id: String(i + 1),
@@ -63,7 +63,7 @@ describe("result-parser performance", () => {
63
63
  }
64
64
 
65
65
  describe("simple record processing", () => {
66
- it("10,000 레코드 processing - 500ms 이내", async () => {
66
+ it("10,000 records processing - within 500ms", async () => {
67
67
  const raw = generateSimpleRecords(10_000);
68
68
  const meta: ResultMeta = {
69
69
  columns: {
@@ -82,10 +82,10 @@ describe("result-parser performance", () => {
82
82
 
83
83
  expect(result).toHaveLength(10_000);
84
84
  expect(elapsed).toBeLessThan(500);
85
- console.log(` 단순 10,000개: ${elapsed.toFixed(2)}ms`);
85
+ console.log(` simple 10,000: ${elapsed.toFixed(2)}ms`);
86
86
  });
87
87
 
88
- it("50,000 레코드 processing - 3000ms 이내", async () => {
88
+ it("50,000 records processing - within 3000ms", async () => {
89
89
  const raw = generateSimpleRecords(50_000);
90
90
  const meta: ResultMeta = {
91
91
  columns: {
@@ -104,12 +104,12 @@ describe("result-parser performance", () => {
104
104
 
105
105
  expect(result).toHaveLength(50_000);
106
106
  expect(elapsed).toBeLessThan(3000);
107
- console.log(` 단순 50,000개: ${elapsed.toFixed(2)}ms`);
107
+ console.log(` simple 50,000: ${elapsed.toFixed(2)}ms`);
108
108
  });
109
109
  });
110
110
 
111
111
  describe("1-level JOIN processing", () => {
112
- it("1,000 users × 10 posts = 10,000 레코드 - 600ms 이내", async () => {
112
+ it("1,000 users × 10 posts = 10,000 records - within 600ms", async () => {
113
113
  const raw = generateJoinRecords(1_000, 10);
114
114
  const meta: ResultMeta = {
115
115
  columns: {
@@ -128,10 +128,10 @@ describe("result-parser performance", () => {
128
128
 
129
129
  expect(result).toHaveLength(1_000);
130
130
  expect(elapsed).toBeLessThan(600);
131
- console.log(` JOIN 10,000 (1000×10): ${elapsed.toFixed(2)}ms`);
131
+ console.log(` JOIN 10,000 (1000×10): ${elapsed.toFixed(2)}ms`);
132
132
  });
133
133
 
134
- it("5,000 users × 10 posts = 50,000 레코드 - 3000ms 이내", async () => {
134
+ it("5,000 users × 10 posts = 50,000 records - within 3000ms", async () => {
135
135
  const raw = generateJoinRecords(5_000, 10);
136
136
  const meta: ResultMeta = {
137
137
  columns: {
@@ -150,12 +150,12 @@ describe("result-parser performance", () => {
150
150
 
151
151
  expect(result).toHaveLength(5_000);
152
152
  expect(elapsed).toBeLessThan(3000);
153
- console.log(` JOIN 50,000 (5000×10): ${elapsed.toFixed(2)}ms`);
153
+ console.log(` JOIN 50,000 (5000×10): ${elapsed.toFixed(2)}ms`);
154
154
  });
155
155
  });
156
156
 
157
157
  describe("2-level nested JOIN processing", () => {
158
- it("100 users × 10 posts × 5 comments = 5,000 레코드 - 500ms 이내", async () => {
158
+ it("100 users × 10 posts × 5 comments = 5,000 records - within 500ms", async () => {
159
159
  const raw = generateNestedJoinRecords(100, 10, 5);
160
160
  const meta: ResultMeta = {
161
161
  columns: {
@@ -178,10 +178,10 @@ describe("result-parser performance", () => {
178
178
 
179
179
  expect(result).toHaveLength(100);
180
180
  expect(elapsed).toBeLessThan(500);
181
- console.log(` 중첩 JOIN 5,000 (100×10×5): ${elapsed.toFixed(2)}ms`);
181
+ console.log(` nested JOIN 5,000 (100×10×5): ${elapsed.toFixed(2)}ms`);
182
182
  });
183
183
 
184
- it("500 users × 10 posts × 5 comments = 25,000 레코드 - 2000ms 이내", async () => {
184
+ it("500 users × 10 posts × 5 comments = 25,000 records - within 2000ms", async () => {
185
185
  const raw = generateNestedJoinRecords(500, 10, 5);
186
186
  const meta: ResultMeta = {
187
187
  columns: {
@@ -204,7 +204,7 @@ describe("result-parser performance", () => {
204
204
 
205
205
  expect(result).toHaveLength(500);
206
206
  expect(elapsed).toBeLessThan(2000);
207
- console.log(` 중첩 JOIN 25,000 (500×10×5): ${elapsed.toFixed(2)}ms`);
207
+ console.log(` nested JOIN 25,000 (500×10×5): ${elapsed.toFixed(2)}ms`);
208
208
  });
209
209
  });
210
210
  });
@@ -4,7 +4,7 @@ import { parseQueryResult } from "../../src/utils/result-parser";
4
4
  import type { ResultMeta } from "../../src/types/db";
5
5
 
6
6
  describe("result-parser", () => {
7
- //#region ========== 타입 파싱 ==========
7
+ //#region ========== type parsing ==========
8
8
 
9
9
  describe("type parsing", () => {
10
10
  it("number conversion", async () => {
@@ -51,7 +51,7 @@ describe("result-parser", () => {
51
51
  expect(result).toEqual([{ active: true, deleted: false }]);
52
52
  });
53
53
 
54
- it('boolean conversion - "0"/"1" 문자열', async () => {
54
+ it('boolean conversion - "0"/"1" strings', async () => {
55
55
  const raw = [{ active: "1", deleted: "0" }];
56
56
  const meta: ResultMeta = {
57
57
  columns: { active: "boolean", deleted: "boolean" },
@@ -107,7 +107,7 @@ describe("result-parser", () => {
107
107
  expect(result![0].startTime.second).toBe(0);
108
108
  });
109
109
 
110
- it("Uuid conversion - 문자열", async () => {
110
+ it("Uuid conversion - string", async () => {
111
111
  const uuidStr = "550e8400-e29b-41d4-a716-446655440000";
112
112
  const raw = [{ id: uuidStr }];
113
113
  const meta: ResultMeta = {
@@ -136,7 +136,7 @@ describe("result-parser", () => {
136
136
  expect(result![0].id.toString()).toBe(uuidStr);
137
137
  });
138
138
 
139
- it("Bytes conversion - Uint8Array 그대로", async () => {
139
+ it("Bytes conversion - Uint8Array passthrough", async () => {
140
140
  const bytes = new Uint8Array([0x01, 0x02, 0x03]);
141
141
  const raw = [{ data: bytes }];
142
142
  const meta: ResultMeta = {
@@ -150,8 +150,8 @@ describe("result-parser", () => {
150
150
  expect(Array.from(result![0].data)).toEqual([0x01, 0x02, 0x03]);
151
151
  });
152
152
 
153
- it("Bytes conversion - hex 문자열", async () => {
154
- // [0x01, 0x02, 0x03]의 hex 표현
153
+ it("Bytes conversion - hex string", async () => {
154
+ // hex representation of [0x01, 0x02, 0x03]
155
155
  const hexStr = "010203";
156
156
  const raw = [{ data: hexStr }];
157
157
  const meta: ResultMeta = {
@@ -171,7 +171,7 @@ describe("result-parser", () => {
171
171
  //#region ========== null/undefined processing ==========
172
172
 
173
173
  describe("null/undefined processing", () => {
174
- it("null 값은 키가 제거됨", async () => {
174
+ it("null value removes the key", async () => {
175
175
  const raw = [{ id: 1, name: null }];
176
176
  const meta: ResultMeta = {
177
177
  columns: { id: "number", name: "string" },
@@ -183,7 +183,7 @@ describe("result-parser", () => {
183
183
  expect(result![0]).not.toHaveProperty("name");
184
184
  });
185
185
 
186
- it("undefined 값은 키가 제거됨", async () => {
186
+ it("undefined value removes the key", async () => {
187
187
  const raw = [{ id: 1, name: undefined }];
188
188
  const meta: ResultMeta = {
189
189
  columns: { id: "number", name: "string" },
@@ -195,7 +195,7 @@ describe("result-parser", () => {
195
195
  expect(result![0]).not.toHaveProperty("name");
196
196
  });
197
197
 
198
- it("모든 값이 null 레코드는 제외됨", async () => {
198
+ it("record with all null values is excluded", async () => {
199
199
  const raw = [{ id: null, name: null }];
200
200
  const meta: ResultMeta = {
201
201
  columns: { id: "number", name: "string" },
@@ -206,7 +206,7 @@ describe("result-parser", () => {
206
206
  expect(result).toBeUndefined();
207
207
  });
208
208
 
209
- it(" 배열은 undefined 반환", async () => {
209
+ it("empty array returns undefined", async () => {
210
210
  const raw: Record<string, unknown>[] = [];
211
211
  const meta: ResultMeta = {
212
212
  columns: { id: "number" },
@@ -217,8 +217,8 @@ describe("result-parser", () => {
217
217
  expect(result).toBeUndefined();
218
218
  });
219
219
 
220
- it("columns 없는 column은 무시", async () => {
221
- const raw = [{ id: 1, name: "Gildong Hong", extra: "무시됨" }];
220
+ it("columns not in meta are ignored", async () => {
221
+ const raw = [{ id: 1, name: "Gildong Hong", extra: "ignored" }];
222
222
  const meta: ResultMeta = {
223
223
  columns: { id: "number", name: "string" },
224
224
  joins: {},
@@ -232,7 +232,7 @@ describe("result-parser", () => {
232
232
 
233
233
  //#endregion
234
234
 
235
- //#region ========== 에러 processing ==========
235
+ //#region ========== error processing ==========
236
236
 
237
237
  describe("error processing", () => {
238
238
  it("number throw if parsing fails", async () => {
@@ -266,7 +266,7 @@ describe("result-parser", () => {
266
266
  });
267
267
 
268
268
  it("Bytes throw if parsing fails", async () => {
269
- const raw = [{ data: 12345 }]; // Number Bytes로 conversion 불가
269
+ const raw = [{ data: 12345 }]; // Number cannot be converted to Bytes
270
270
  const meta: ResultMeta = {
271
271
  columns: { data: "Bytes" },
272
272
  joins: {},
@@ -278,10 +278,10 @@ describe("result-parser", () => {
278
278
 
279
279
  //#endregion
280
280
 
281
- //#region ========== 1레벨 JOIN ==========
281
+ //#region ========== 1-level JOIN ==========
282
282
 
283
283
  describe("1-level JOIN", () => {
284
- it("isSingle: true - 단일 객체", async () => {
284
+ it("isSingle: true - single object", async () => {
285
285
  const raw = [{ "id": 1, "name": "User1", "company.id": 100, "company.name": "Corp" }];
286
286
  const meta: ResultMeta = {
287
287
  columns: {
@@ -303,7 +303,7 @@ describe("result-parser", () => {
303
303
  ]);
304
304
  });
305
305
 
306
- it("isSingle: false - 배열", async () => {
306
+ it("isSingle: false - array", async () => {
307
307
  const raw = [
308
308
  { "id": 1, "name": "User1", "posts.id": 10, "posts.title": "Post1" },
309
309
  { "id": 1, "name": "User1", "posts.id": 11, "posts.title": "Post2" },
@@ -331,7 +331,7 @@ describe("result-parser", () => {
331
331
  ]);
332
332
  });
333
333
 
334
- it("isSingle: true - 여러 개의 다른 결과 throw", async () => {
334
+ it("isSingle: true - throws when multiple different results exist", async () => {
335
335
  const raw = [
336
336
  { "id": 1, "name": "User1", "company.id": 100, "company.name": "Corp1" },
337
337
  { "id": 1, "name": "User1", "company.id": 200, "company.name": "Corp2" },
@@ -351,7 +351,7 @@ describe("result-parser", () => {
351
351
  );
352
352
  });
353
353
 
354
- it(" JOIN 결과는 키가 N/A", async () => {
354
+ it("empty JOIN result omits the key", async () => {
355
355
  const raw = [{ "id": 1, "name": "User1", "company.id": null, "company.name": null }];
356
356
  const meta: ResultMeta = {
357
357
  columns: {
@@ -368,7 +368,7 @@ describe("result-parser", () => {
368
368
  expect(result![0]).not.toHaveProperty("company");
369
369
  });
370
370
 
371
- it("여러 루트 레코드와 JOIN", async () => {
371
+ it("multiple root records with JOIN", async () => {
372
372
  const raw = [
373
373
  { "id": 1, "name": "User1", "posts.id": 10, "posts.title": "Post1" },
374
374
  { "id": 2, "name": "User2", "posts.id": 20, "posts.title": "Post2" },
@@ -405,10 +405,10 @@ describe("result-parser", () => {
405
405
 
406
406
  //#endregion
407
407
 
408
- //#region ========== 다중 레벨 JOIN ==========
408
+ //#region ========== multi-level JOIN ==========
409
409
 
410
410
  describe("multi-level JOIN", () => {
411
- it("2레벨 중첩", async () => {
411
+ it("2-level nesting", async () => {
412
412
  const raw = [
413
413
  {
414
414
  "id": 1,
@@ -461,7 +461,7 @@ describe("result-parser", () => {
461
461
  ]);
462
462
  });
463
463
 
464
- it("3레벨 중첩", async () => {
464
+ it("3-level nesting", async () => {
465
465
  const raw = [
466
466
  {
467
467
  "id": 1,
@@ -527,7 +527,7 @@ describe("result-parser", () => {
527
527
  ]);
528
528
  });
529
529
 
530
- it("여러 JOIN과 혼합 isSingle", async () => {
530
+ it("multiple JOINs with mixed isSingle", async () => {
531
531
  const raw = [
532
532
  {
533
533
  "id": 1,
@@ -575,7 +575,7 @@ describe("result-parser", () => {
575
575
  ]);
576
576
  });
577
577
 
578
- it("여러 JOIN 일부만 NULL - company 있고 posts N/A", async () => {
578
+ it("partial NULL among multiple JOINs - company present, posts absent", async () => {
579
579
  const raw = [
580
580
  {
581
581
  "id": 1,
@@ -615,10 +615,10 @@ describe("result-parser", () => {
615
615
 
616
616
  //#endregion
617
617
 
618
- //#region ========== 에지 케이스 ==========
618
+ //#region ========== edge cases ==========
619
619
 
620
620
  describe("edge cases", () => {
621
- it("joins 객체인 경우", async () => {
621
+ it("joins is an empty object", async () => {
622
622
  const raw = [{ id: 1, name: "User1" }];
623
623
  const meta: ResultMeta = {
624
624
  columns: { id: "number", name: "string" },
@@ -629,10 +629,10 @@ describe("result-parser", () => {
629
629
  expect(result).toEqual([{ id: 1, name: "User1" }]);
630
630
  });
631
631
 
632
- it("중복 데이터 제거", async () => {
632
+ it("deduplicates duplicate data", async () => {
633
633
  const raw = [
634
634
  { "id": 1, "name": "User1", "posts.id": 10, "posts.title": "Post1" },
635
- { "id": 1, "name": "User1", "posts.id": 10, "posts.title": "Post1" }, // 중복
635
+ { "id": 1, "name": "User1", "posts.id": 10, "posts.title": "Post1" }, // duplicate
636
636
  ];
637
637
  const meta: ResultMeta = {
638
638
  columns: {
@@ -649,15 +649,15 @@ describe("result-parser", () => {
649
649
  {
650
650
  id: 1,
651
651
  name: "User1",
652
- posts: [{ id: 10, title: "Post1" }], // 중복 제거됨
652
+ posts: [{ id: 10, title: "Post1" }], // deduplicated
653
653
  },
654
654
  ]);
655
655
  });
656
656
 
657
- it("isSingle: true - 동일한 데이터는 에러 N/A", async () => {
657
+ it("isSingle: true - identical data does not throw", async () => {
658
658
  const raw = [
659
659
  { "id": 1, "name": "User1", "company.id": 100, "company.name": "Corp" },
660
- { "id": 1, "name": "User1", "company.id": 100, "company.name": "Corp" }, // 동일
660
+ { "id": 1, "name": "User1", "company.id": 100, "company.name": "Corp" }, // identical
661
661
  ];
662
662
  const meta: ResultMeta = {
663
663
  columns: {
@@ -679,8 +679,8 @@ describe("result-parser", () => {
679
679
  ]);
680
680
  });
681
681
 
682
- it("대용량 데이터 yield processing", async () => {
683
- // 250 레코드 생성 (yield 2회 발생: i=100, i=200)
682
+ it("large dataset yield processing", async () => {
683
+ // generates 250 records (yield fires twice: at i=100, i=200)
684
684
  const raw = Array.from({ length: 250 }, (_, i) => ({
685
685
  id: String(i + 1),
686
686
  name: `User${i + 1}`,