@simplysm/core-common 13.0.0-beta.2 → 13.0.0-beta.21

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.
Files changed (84) hide show
  1. package/dist/common.types.js +4 -4
  2. package/dist/errors/argument-error.js +1 -1
  3. package/dist/errors/not-implemented-error.js +1 -1
  4. package/dist/errors/timeout-error.js +1 -1
  5. package/dist/extensions/arr-ext.helpers.js +4 -4
  6. package/dist/extensions/arr-ext.js +9 -9
  7. package/dist/features/debounce-queue.js +2 -2
  8. package/dist/features/serial-queue.js +3 -3
  9. package/dist/index.js +30 -30
  10. package/dist/types/date-only.js +2 -2
  11. package/dist/types/date-time.js +2 -2
  12. package/dist/types/time.js +2 -2
  13. package/dist/types/uuid.js +1 -1
  14. package/dist/utils/bytes.js +1 -1
  15. package/dist/utils/json.js +8 -8
  16. package/dist/utils/obj.js +5 -5
  17. package/dist/utils/primitive.js +5 -5
  18. package/dist/utils/transferable.js +4 -4
  19. package/dist/utils/wait.js +1 -1
  20. package/package.json +7 -4
  21. package/.cache/typecheck-browser.tsbuildinfo +0 -1
  22. package/.cache/typecheck-node.tsbuildinfo +0 -1
  23. package/.cache/typecheck-tests-browser.tsbuildinfo +0 -1
  24. package/.cache/typecheck-tests-node.tsbuildinfo +0 -1
  25. package/src/common.types.ts +0 -91
  26. package/src/env.ts +0 -11
  27. package/src/errors/argument-error.ts +0 -40
  28. package/src/errors/not-implemented-error.ts +0 -32
  29. package/src/errors/sd-error.ts +0 -53
  30. package/src/errors/timeout-error.ts +0 -36
  31. package/src/extensions/arr-ext.helpers.ts +0 -53
  32. package/src/extensions/arr-ext.ts +0 -777
  33. package/src/extensions/arr-ext.types.ts +0 -258
  34. package/src/extensions/map-ext.ts +0 -86
  35. package/src/extensions/set-ext.ts +0 -68
  36. package/src/features/debounce-queue.ts +0 -116
  37. package/src/features/event-emitter.ts +0 -112
  38. package/src/features/serial-queue.ts +0 -94
  39. package/src/globals.ts +0 -12
  40. package/src/index.ts +0 -55
  41. package/src/types/date-only.ts +0 -329
  42. package/src/types/date-time.ts +0 -294
  43. package/src/types/lazy-gc-map.ts +0 -244
  44. package/src/types/time.ts +0 -210
  45. package/src/types/uuid.ts +0 -113
  46. package/src/utils/bytes.ts +0 -160
  47. package/src/utils/date-format.ts +0 -239
  48. package/src/utils/json.ts +0 -230
  49. package/src/utils/num.ts +0 -97
  50. package/src/utils/obj.ts +0 -956
  51. package/src/utils/path.ts +0 -40
  52. package/src/utils/primitive.ts +0 -33
  53. package/src/utils/str.ts +0 -252
  54. package/src/utils/template-strings.ts +0 -132
  55. package/src/utils/transferable.ts +0 -269
  56. package/src/utils/wait.ts +0 -40
  57. package/src/utils/xml.ts +0 -105
  58. package/src/zip/sd-zip.ts +0 -218
  59. package/tests/errors/errors.spec.ts +0 -196
  60. package/tests/extensions/array-extension.spec.ts +0 -790
  61. package/tests/extensions/map-extension.spec.ts +0 -147
  62. package/tests/extensions/set-extension.spec.ts +0 -74
  63. package/tests/types/date-only.spec.ts +0 -636
  64. package/tests/types/date-time.spec.ts +0 -391
  65. package/tests/types/lazy-gc-map.spec.ts +0 -692
  66. package/tests/types/time.spec.ts +0 -559
  67. package/tests/types/types.spec.ts +0 -55
  68. package/tests/types/uuid.spec.ts +0 -91
  69. package/tests/utils/bytes-utils.spec.ts +0 -230
  70. package/tests/utils/date-format.spec.ts +0 -371
  71. package/tests/utils/debounce-queue.spec.ts +0 -272
  72. package/tests/utils/json.spec.ts +0 -475
  73. package/tests/utils/number.spec.ts +0 -184
  74. package/tests/utils/object.spec.ts +0 -827
  75. package/tests/utils/path.spec.ts +0 -78
  76. package/tests/utils/primitive.spec.ts +0 -55
  77. package/tests/utils/sd-event-emitter.spec.ts +0 -216
  78. package/tests/utils/serial-queue.spec.ts +0 -365
  79. package/tests/utils/string.spec.ts +0 -294
  80. package/tests/utils/template-strings.spec.ts +0 -96
  81. package/tests/utils/transferable.spec.ts +0 -698
  82. package/tests/utils/wait.spec.ts +0 -145
  83. package/tests/utils/xml.spec.ts +0 -146
  84. package/tests/zip/sd-zip.spec.ts +0 -234
@@ -1,78 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { pathJoin, pathBasename, pathExtname } from "@simplysm/core-common";
3
-
4
- describe("path utils", () => {
5
- describe("pathJoin()", () => {
6
- it("경로 세그먼트를 결합한다", () => {
7
- expect(pathJoin("a", "b", "c")).toBe("a/b/c");
8
- });
9
-
10
- it("선행 슬래시를 유지한다", () => {
11
- expect(pathJoin("/a", "b")).toBe("/a/b");
12
- });
13
-
14
- it("중복 슬래시를 제거한다", () => {
15
- expect(pathJoin("a/", "/b/", "/c")).toBe("a/b/c");
16
- });
17
-
18
- it("빈 세그먼트를 무시한다", () => {
19
- expect(pathJoin("a", "", "b")).toBe("a/b");
20
- });
21
-
22
- it("단일 세그먼트를 반환한다", () => {
23
- expect(pathJoin("a")).toBe("a");
24
- });
25
-
26
- it("빈 입력은 빈 문자열을 반환한다", () => {
27
- expect(pathJoin()).toBe("");
28
- });
29
- });
30
-
31
- describe("pathBasename()", () => {
32
- it("파일명을 추출한다", () => {
33
- expect(pathBasename("a/b/file.txt")).toBe("file.txt");
34
- });
35
-
36
- it("확장자를 제거한다", () => {
37
- expect(pathBasename("a/b/file.txt", ".txt")).toBe("file");
38
- });
39
-
40
- it("일치하지 않는 확장자는 무시한다", () => {
41
- expect(pathBasename("a/b/file.txt", ".md")).toBe("file.txt");
42
- });
43
-
44
- it("경로 없는 파일명을 처리한다", () => {
45
- expect(pathBasename("file.txt")).toBe("file.txt");
46
- });
47
-
48
- it("빈 문자열은 빈 문자열을 반환한다", () => {
49
- expect(pathBasename("")).toBe("");
50
- });
51
- });
52
-
53
- describe("pathExtname()", () => {
54
- it("확장자를 추출한다", () => {
55
- expect(pathExtname("file.txt")).toBe(".txt");
56
- });
57
-
58
- it("마지막 확장자만 추출한다", () => {
59
- expect(pathExtname("archive.tar.gz")).toBe(".gz");
60
- });
61
-
62
- it("확장자 없는 파일은 빈 문자열을 반환한다", () => {
63
- expect(pathExtname("Makefile")).toBe("");
64
- });
65
-
66
- it("숨김 파일은 빈 문자열을 반환한다", () => {
67
- expect(pathExtname(".gitignore")).toBe("");
68
- });
69
-
70
- it("경로 포함 파일의 확장자를 추출한다", () => {
71
- expect(pathExtname("a/b/file.ts")).toBe(".ts");
72
- });
73
-
74
- it("빈 문자열은 빈 문자열을 반환한다", () => {
75
- expect(pathExtname("")).toBe("");
76
- });
77
- });
78
- });
@@ -1,55 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { getPrimitiveTypeStr, DateTime, DateOnly, Time, Uuid } from "@simplysm/core-common";
3
-
4
- describe("primitive utils", () => {
5
- describe("getPrimitiveTypeStr()", () => {
6
- it("string을 반환한다", () => {
7
- expect(getPrimitiveTypeStr("hello")).toBe("string");
8
- });
9
-
10
- it("빈 문자열도 string을 반환한다", () => {
11
- expect(getPrimitiveTypeStr("")).toBe("string");
12
- });
13
-
14
- it("number를 반환한다", () => {
15
- expect(getPrimitiveTypeStr(42)).toBe("number");
16
- });
17
-
18
- it("0도 number를 반환한다", () => {
19
- expect(getPrimitiveTypeStr(0)).toBe("number");
20
- });
21
-
22
- it("NaN도 number를 반환한다", () => {
23
- expect(getPrimitiveTypeStr(NaN)).toBe("number");
24
- });
25
-
26
- it("boolean을 반환한다", () => {
27
- expect(getPrimitiveTypeStr(true)).toBe("boolean");
28
- expect(getPrimitiveTypeStr(false)).toBe("boolean");
29
- });
30
-
31
- it("DateTime을 반환한다", () => {
32
- expect(getPrimitiveTypeStr(new DateTime())).toBe("DateTime");
33
- });
34
-
35
- it("DateOnly를 반환한다", () => {
36
- expect(getPrimitiveTypeStr(new DateOnly())).toBe("DateOnly");
37
- });
38
-
39
- it("Time을 반환한다", () => {
40
- expect(getPrimitiveTypeStr(new Time())).toBe("Time");
41
- });
42
-
43
- it("Uuid를 반환한다", () => {
44
- expect(getPrimitiveTypeStr(Uuid.new())).toBe("Uuid");
45
- });
46
-
47
- it("Uint8Array는 Bytes를 반환한다", () => {
48
- expect(getPrimitiveTypeStr(new Uint8Array([1, 2]))).toBe("Bytes");
49
- });
50
-
51
- it("지원하지 않는 타입은 에러를 던진다", () => {
52
- expect(() => getPrimitiveTypeStr({} as never)).toThrow();
53
- });
54
- });
55
- });
@@ -1,216 +0,0 @@
1
- import { describe, it, expect, vi } from "vitest";
2
- import { EventEmitter } from "@simplysm/core-common";
3
-
4
- interface TestEvents {
5
- message: string;
6
- count: number;
7
- data: { id: number; name: string };
8
- empty: void;
9
- }
10
-
11
- describe("SdEventEmitter", () => {
12
- //#region on/emit
13
-
14
- describe("on() / emit()", () => {
15
- it("이벤트를 발생시키고 수신한다", () => {
16
- const emitter = new EventEmitter<TestEvents>();
17
- const listener = vi.fn();
18
-
19
- emitter.on("message", listener);
20
- emitter.emit("message", "hello");
21
-
22
- expect(listener).toHaveBeenCalledWith("hello");
23
- expect(listener).toHaveBeenCalledTimes(1);
24
- });
25
-
26
- it("여러 번 emit하면 리스너가 여러 번 호출된다", () => {
27
- const emitter = new EventEmitter<TestEvents>();
28
- const listener = vi.fn();
29
-
30
- emitter.on("count", listener);
31
- emitter.emit("count", 1);
32
- emitter.emit("count", 2);
33
- emitter.emit("count", 3);
34
-
35
- expect(listener).toHaveBeenCalledTimes(3);
36
- expect(listener).toHaveBeenNthCalledWith(1, 1);
37
- expect(listener).toHaveBeenNthCalledWith(2, 2);
38
- expect(listener).toHaveBeenNthCalledWith(3, 3);
39
- });
40
-
41
- it("객체 데이터를 전달한다", () => {
42
- const emitter = new EventEmitter<TestEvents>();
43
- const listener = vi.fn();
44
-
45
- emitter.on("data", listener);
46
- emitter.emit("data", { id: 1, name: "test" });
47
-
48
- expect(listener).toHaveBeenCalledWith({ id: 1, name: "test" });
49
- });
50
-
51
- it("void 이벤트를 처리한다", () => {
52
- const emitter = new EventEmitter<TestEvents>();
53
- const listener = vi.fn();
54
-
55
- emitter.on("empty", listener);
56
- emitter.emit("empty");
57
-
58
- expect(listener).toHaveBeenCalledTimes(1);
59
- });
60
-
61
- it("같은 이벤트에 여러 리스너를 등록할 수 있다", () => {
62
- const emitter = new EventEmitter<TestEvents>();
63
- const listener1 = vi.fn();
64
- const listener2 = vi.fn();
65
-
66
- emitter.on("message", listener1);
67
- emitter.on("message", listener2);
68
- emitter.emit("message", "test");
69
-
70
- expect(listener1).toHaveBeenCalledWith("test");
71
- expect(listener2).toHaveBeenCalledWith("test");
72
- });
73
- });
74
-
75
- //#endregion
76
-
77
- //#region off
78
-
79
- describe("off()", () => {
80
- it("리스너를 해제한다", () => {
81
- const emitter = new EventEmitter<TestEvents>();
82
- const listener = vi.fn();
83
-
84
- emitter.on("message", listener);
85
- emitter.off("message", listener);
86
- emitter.emit("message", "test");
87
-
88
- expect(listener).not.toHaveBeenCalled();
89
- });
90
-
91
- it("해제된 리스너만 제거되고 다른 리스너는 유지된다", () => {
92
- const emitter = new EventEmitter<TestEvents>();
93
- const listener1 = vi.fn();
94
- const listener2 = vi.fn();
95
-
96
- emitter.on("message", listener1);
97
- emitter.on("message", listener2);
98
- emitter.off("message", listener1);
99
- emitter.emit("message", "test");
100
-
101
- expect(listener1).not.toHaveBeenCalled();
102
- expect(listener2).toHaveBeenCalledWith("test");
103
- });
104
-
105
- it("등록되지 않은 리스너를 해제해도 에러가 발생하지 않는다", () => {
106
- const emitter = new EventEmitter<TestEvents>();
107
- const listener = vi.fn();
108
-
109
- // 에러 없이 실행되어야 함
110
- expect(() => {
111
- emitter.off("message", listener);
112
- }).not.toThrow();
113
- });
114
- });
115
-
116
- //#endregion
117
-
118
- //#region listenerCount
119
-
120
- describe("listenerCount()", () => {
121
- it("리스너 수를 정확히 카운트한다", () => {
122
- const emitter = new EventEmitter<TestEvents>();
123
-
124
- expect(emitter.listenerCount("message")).toBe(0);
125
-
126
- emitter.on("message", () => {});
127
- expect(emitter.listenerCount("message")).toBe(1);
128
-
129
- emitter.on("message", () => {});
130
- expect(emitter.listenerCount("message")).toBe(2);
131
- });
132
-
133
- it("off 후 카운트가 감소한다", () => {
134
- const emitter = new EventEmitter<TestEvents>();
135
- const listener = vi.fn();
136
-
137
- emitter.on("message", listener);
138
- expect(emitter.listenerCount("message")).toBe(1);
139
-
140
- emitter.off("message", listener);
141
- expect(emitter.listenerCount("message")).toBe(0);
142
- });
143
-
144
- it("등록되지 않은 이벤트 타입의 카운트는 0이다", () => {
145
- const emitter = new EventEmitter<TestEvents>();
146
-
147
- expect(emitter.listenerCount("message")).toBe(0);
148
- expect(emitter.listenerCount("count")).toBe(0);
149
- });
150
-
151
- it("다른 이벤트 타입의 카운트는 독립적이다", () => {
152
- const emitter = new EventEmitter<TestEvents>();
153
-
154
- emitter.on("message", () => {});
155
- emitter.on("message", () => {});
156
- emitter.on("count", () => {});
157
-
158
- expect(emitter.listenerCount("message")).toBe(2);
159
- expect(emitter.listenerCount("count")).toBe(1);
160
- });
161
- });
162
-
163
- //#endregion
164
-
165
- //#region 중복 등록 방지
166
-
167
- describe("중복 등록 방지", () => {
168
- it("동일한 리스너를 중복 등록하면 무시된다", () => {
169
- const emitter = new EventEmitter<TestEvents>();
170
- const listener = vi.fn();
171
-
172
- emitter.on("message", listener);
173
- emitter.on("message", listener); // 중복 등록 시도
174
- emitter.on("message", listener); // 중복 등록 시도
175
-
176
- expect(emitter.listenerCount("message")).toBe(1);
177
-
178
- emitter.emit("message", "test");
179
- expect(listener).toHaveBeenCalledTimes(1);
180
- });
181
-
182
- it("동일한 리스너를 서로 다른 이벤트에 등록할 수 있다", () => {
183
- const emitter = new EventEmitter<TestEvents>();
184
- const listener = vi.fn();
185
-
186
- emitter.on("message", listener);
187
- emitter.on("count", listener as any);
188
-
189
- expect(emitter.listenerCount("message")).toBe(1);
190
- expect(emitter.listenerCount("count")).toBe(1);
191
-
192
- emitter.emit("message", "test");
193
- emitter.emit("count", 123);
194
-
195
- expect(listener).toHaveBeenCalledTimes(2);
196
- expect(listener).toHaveBeenNthCalledWith(1, "test");
197
- expect(listener).toHaveBeenNthCalledWith(2, 123);
198
- });
199
-
200
- it("중복 등록 후 off 한 번 호출로 제거된다", () => {
201
- const emitter = new EventEmitter<TestEvents>();
202
- const listener = vi.fn();
203
-
204
- emitter.on("message", listener);
205
- emitter.on("message", listener); // 중복 등록 시도
206
-
207
- emitter.off("message", listener);
208
- expect(emitter.listenerCount("message")).toBe(0);
209
-
210
- emitter.emit("message", "test");
211
- expect(listener).not.toHaveBeenCalled();
212
- });
213
- });
214
-
215
- //#endregion
216
- });
@@ -1,365 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { SerialQueue, SdError } from "@simplysm/core-common";
3
-
4
- describe("SerialQueue", () => {
5
- beforeEach(() => {
6
- vi.useFakeTimers();
7
- });
8
-
9
- afterEach(() => {
10
- vi.useRealTimers();
11
- });
12
-
13
- //#region 순차 실행
14
-
15
- describe("순차 실행", () => {
16
- it("큐에 추가된 함수들을 순서대로 실행한다", async () => {
17
- const queue = new SerialQueue();
18
- const calls: number[] = [];
19
-
20
- queue.run(() => {
21
- calls.push(1);
22
- });
23
- queue.run(() => {
24
- calls.push(2);
25
- });
26
- queue.run(() => {
27
- calls.push(3);
28
- });
29
-
30
- await vi.advanceTimersByTimeAsync(50);
31
-
32
- expect(calls).toEqual([1, 2, 3]);
33
- });
34
-
35
- it("이전 작업이 완료된 후 다음 작업을 실행한다", async () => {
36
- const queue = new SerialQueue();
37
- const calls: number[] = [];
38
- const timestamps: number[] = [];
39
-
40
- queue.run(async () => {
41
- timestamps.push(Date.now());
42
- calls.push(1);
43
- await new Promise((r) => setTimeout(r, 50));
44
- });
45
- queue.run(async () => {
46
- timestamps.push(Date.now());
47
- calls.push(2);
48
- await new Promise((r) => setTimeout(r, 50));
49
- });
50
-
51
- await vi.advanceTimersByTimeAsync(200);
52
-
53
- expect(calls).toEqual([1, 2]);
54
- // 두 번째 작업은 첫 작업 완료 후 시작 (정확히 50ms)
55
- expect(timestamps[1] - timestamps[0]).toBe(50);
56
- });
57
-
58
- it("실행 중에 추가된 작업도 순차적으로 실행한다", async () => {
59
- const queue = new SerialQueue();
60
- const calls: number[] = [];
61
-
62
- queue.run(async () => {
63
- calls.push(1);
64
- await new Promise((r) => setTimeout(r, 50));
65
- });
66
-
67
- // 첫 작업 시작 대기
68
- await vi.advanceTimersByTimeAsync(10);
69
-
70
- // 실행 중에 추가
71
- queue.run(() => {
72
- calls.push(2);
73
- });
74
-
75
- await vi.advanceTimersByTimeAsync(100);
76
-
77
- expect(calls).toEqual([1, 2]);
78
- });
79
- });
80
-
81
- //#endregion
82
-
83
- //#region gap 간격
84
-
85
- describe("gap 간격", () => {
86
- it("gap이 설정되면 작업 사이에 대기한다", async () => {
87
- const queue = new SerialQueue(50);
88
- const timestamps: number[] = [];
89
-
90
- queue.run(() => {
91
- timestamps.push(Date.now());
92
- });
93
- queue.run(() => {
94
- timestamps.push(Date.now());
95
- });
96
-
97
- await vi.advanceTimersByTimeAsync(150);
98
-
99
- expect(timestamps).toHaveLength(2);
100
- // 두 작업 사이에 정확히 50ms 간격
101
- expect(timestamps[1] - timestamps[0]).toBe(50);
102
- });
103
-
104
- it("gap이 0이면 즉시 다음 작업을 실행한다", async () => {
105
- const queue = new SerialQueue(0);
106
- const timestamps: number[] = [];
107
-
108
- queue.run(() => {
109
- timestamps.push(Date.now());
110
- });
111
- queue.run(() => {
112
- timestamps.push(Date.now());
113
- });
114
-
115
- await vi.advanceTimersByTimeAsync(50);
116
-
117
- expect(timestamps).toHaveLength(2);
118
- // 간격이 0
119
- expect(timestamps[1] - timestamps[0]).toBe(0);
120
- });
121
-
122
- it("gap은 기본값 0이다", async () => {
123
- const queue = new SerialQueue();
124
- const timestamps: number[] = [];
125
-
126
- queue.run(() => {
127
- timestamps.push(Date.now());
128
- });
129
- queue.run(() => {
130
- timestamps.push(Date.now());
131
- });
132
-
133
- await vi.advanceTimersByTimeAsync(50);
134
-
135
- expect(timestamps).toHaveLength(2);
136
- expect(timestamps[1] - timestamps[0]).toBe(0);
137
- });
138
-
139
- it("마지막 작업 후에는 gap을 대기하지 않는다", async () => {
140
- const queue = new SerialQueue(100);
141
- const timestamps: number[] = [];
142
-
143
- queue.run(() => {
144
- timestamps.push(Date.now());
145
- });
146
- queue.run(() => {
147
- timestamps.push(Date.now());
148
- });
149
-
150
- // 충분히 대기 (작업1 + gap100 + 작업2)
151
- await vi.advanceTimersByTimeAsync(200);
152
-
153
- // gap이 실제로 적용되었는지 확인 (정확히 100ms)
154
- expect(timestamps).toHaveLength(2);
155
- expect(timestamps[1] - timestamps[0]).toBe(100);
156
- });
157
- });
158
-
159
- //#endregion
160
-
161
- //#region 에러 처리
162
-
163
- describe("에러 처리", () => {
164
- it("에러 발생 시 error 이벤트를 발생시킨다", async () => {
165
- const queue = new SerialQueue();
166
- const errors: SdError[] = [];
167
-
168
- queue.on("error", (err) => {
169
- errors.push(err);
170
- });
171
-
172
- queue.run(() => {
173
- throw new Error("test error");
174
- });
175
-
176
- await vi.advanceTimersByTimeAsync(50);
177
-
178
- expect(errors).toHaveLength(1);
179
- expect(errors[0]).toBeInstanceOf(SdError);
180
- expect(errors[0].message).toContain("큐 작업 실행 중 오류 발생");
181
- expect(errors[0].message).toContain("test error");
182
- });
183
-
184
- it("에러가 발생해도 다음 작업은 계속 실행한다", async () => {
185
- const queue = new SerialQueue();
186
- const calls: number[] = [];
187
- const errors: SdError[] = [];
188
-
189
- // 에러 리스너 추가하여 unhandled rejection 방지
190
- queue.on("error", (err) => {
191
- errors.push(err);
192
- });
193
-
194
- queue.run(() => {
195
- calls.push(1);
196
- throw new Error("error");
197
- });
198
- queue.run(() => {
199
- calls.push(2);
200
- });
201
- queue.run(() => {
202
- calls.push(3);
203
- });
204
-
205
- await vi.advanceTimersByTimeAsync(100);
206
-
207
- expect(calls).toEqual([1, 2, 3]);
208
- expect(errors).toHaveLength(1);
209
- });
210
-
211
- it("여러 작업에서 에러가 발생해도 모두 실행한다", async () => {
212
- const queue = new SerialQueue();
213
- const calls: number[] = [];
214
- const errors: SdError[] = [];
215
-
216
- queue.on("error", (err) => {
217
- errors.push(err);
218
- });
219
-
220
- queue.run(() => {
221
- calls.push(1);
222
- throw new Error("error 1");
223
- });
224
- queue.run(() => {
225
- calls.push(2);
226
- });
227
- queue.run(() => {
228
- calls.push(3);
229
- throw new Error("error 3");
230
- });
231
-
232
- await vi.advanceTimersByTimeAsync(100);
233
-
234
- expect(calls).toEqual([1, 2, 3]);
235
- expect(errors).toHaveLength(2);
236
- expect(errors[0].message).toContain("error 1");
237
- expect(errors[1].message).toContain("error 3");
238
- });
239
- });
240
-
241
- //#endregion
242
-
243
- //#region dispose
244
-
245
- describe("dispose()", () => {
246
- it("대기 중인 큐를 비운다", async () => {
247
- const queue = new SerialQueue();
248
- const calls: number[] = [];
249
-
250
- // 첫 작업 실행 중
251
- queue.run(async () => {
252
- calls.push(1);
253
- await new Promise((r) => setTimeout(r, 100));
254
- });
255
-
256
- // 대기 중인 작업들 추가
257
- queue.run(() => {
258
- calls.push(2);
259
- });
260
- queue.run(() => {
261
- calls.push(3);
262
- });
263
-
264
- // 첫 작업 시작 후 dispose
265
- await vi.advanceTimersByTimeAsync(20);
266
- queue.dispose();
267
-
268
- // 모든 작업 완료 대기
269
- await vi.advanceTimersByTimeAsync(150);
270
-
271
- // 첫 작업만 실행됨 (실행 중인 작업은 완료됨)
272
- expect(calls).toEqual([1]);
273
- });
274
-
275
- it("dispose 후 새 작업은 정상 실행된다", async () => {
276
- const queue = new SerialQueue();
277
- const calls: number[] = [];
278
-
279
- queue.run(() => {
280
- calls.push(1);
281
- });
282
- queue.dispose();
283
-
284
- // dispose 후 새 작업 추가
285
- queue.run(() => {
286
- calls.push(2);
287
- });
288
-
289
- await vi.advanceTimersByTimeAsync(50);
290
-
291
- expect(calls).toContain(2);
292
- });
293
-
294
- it("여러 번 호출해도 안전하다", () => {
295
- const queue = new SerialQueue();
296
-
297
- // 여러 번 호출해도 에러 없음
298
- queue.dispose();
299
- queue.dispose();
300
- queue.dispose();
301
- });
302
-
303
- it("using 문으로 자동 dispose된다", async () => {
304
- const calls: number[] = [];
305
- {
306
- using queue = new SerialQueue();
307
- queue.run(async () => {
308
- calls.push(1);
309
- await new Promise((r) => setTimeout(r, 100));
310
- });
311
- queue.run(() => {
312
- calls.push(2);
313
- });
314
- await vi.advanceTimersByTimeAsync(20);
315
- } // using 블록 종료 시 dispose 자동 호출
316
- await vi.advanceTimersByTimeAsync(150);
317
- // 첫 작업(실행 중)은 완료되지만, 대기 중인 작업은 실행되지 않음
318
- expect(calls).toEqual([1]);
319
- });
320
- });
321
-
322
- //#endregion
323
-
324
- //#region 동기 함수 지원
325
-
326
- describe("동기 함수 지원", () => {
327
- it("동기 함수도 실행할 수 있다", async () => {
328
- const queue = new SerialQueue();
329
- const calls: number[] = [];
330
-
331
- queue.run(() => {
332
- calls.push(1);
333
- });
334
- queue.run(() => {
335
- calls.push(2);
336
- });
337
-
338
- await vi.advanceTimersByTimeAsync(50);
339
-
340
- expect(calls).toEqual([1, 2]);
341
- });
342
-
343
- it("동기/비동기 함수를 혼합해서 사용할 수 있다", async () => {
344
- const queue = new SerialQueue();
345
- const calls: number[] = [];
346
-
347
- queue.run(() => {
348
- calls.push(1);
349
- });
350
- queue.run(async () => {
351
- await new Promise((r) => setTimeout(r, 10));
352
- calls.push(2);
353
- });
354
- queue.run(() => {
355
- calls.push(3);
356
- });
357
-
358
- await vi.advanceTimersByTimeAsync(100);
359
-
360
- expect(calls).toEqual([1, 2, 3]);
361
- });
362
- });
363
-
364
- //#endregion
365
- });