@planet-matrix/mobius-model 0.5.0 → 0.9.0
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/CHANGELOG.md +61 -0
- package/README.md +123 -36
- package/dist/index.js +715 -4
- package/dist/index.js.map +981 -13
- package/oxlint.config.ts +6 -0
- package/package.json +36 -18
- package/src/abort/README.md +92 -0
- package/src/abort/abort-manager.ts +278 -0
- package/src/abort/abort-signal-listener-manager.ts +81 -0
- package/src/abort/index.ts +2 -0
- package/src/ai/README.md +1 -0
- package/src/ai/ai.ts +107 -0
- package/src/ai/chat-completion-ai/aihubmix-chat-completion.ts +78 -0
- package/src/ai/chat-completion-ai/chat-completion-ai.ts +270 -0
- package/src/ai/chat-completion-ai/chat-completion.ts +189 -0
- package/src/ai/chat-completion-ai/index.ts +7 -0
- package/src/ai/chat-completion-ai/lingyiwanwu-chat-completion.ts +78 -0
- package/src/ai/chat-completion-ai/ohmygpt-chat-completion.ts +78 -0
- package/src/ai/chat-completion-ai/openai-next-chat-completion.ts +78 -0
- package/src/ai/embedding-ai/embedding-ai.ts +63 -0
- package/src/ai/embedding-ai/embedding.ts +50 -0
- package/src/ai/embedding-ai/index.ts +4 -0
- package/src/ai/embedding-ai/openai-next-embedding.ts +23 -0
- package/src/ai/index.ts +4 -0
- package/src/aio/README.md +100 -0
- package/src/aio/content.ts +141 -0
- package/src/aio/index.ts +3 -0
- package/src/aio/json.ts +127 -0
- package/src/aio/prompt.ts +246 -0
- package/src/basic/README.md +72 -116
- package/src/basic/error.ts +19 -5
- package/src/basic/function.ts +83 -64
- package/src/basic/index.ts +1 -0
- package/src/basic/is.ts +152 -71
- package/src/basic/promise.ts +29 -8
- package/src/basic/schedule.ts +111 -0
- package/src/basic/stream.ts +135 -25
- package/src/basic/string.ts +2 -33
- package/src/color/README.md +105 -0
- package/src/color/index.ts +3 -0
- package/src/color/internal.ts +42 -0
- package/src/color/rgb/analyze.ts +236 -0
- package/src/color/rgb/construct.ts +130 -0
- package/src/color/rgb/convert.ts +227 -0
- package/src/color/rgb/derive.ts +303 -0
- package/src/color/rgb/index.ts +6 -0
- package/src/color/rgb/internal.ts +208 -0
- package/src/color/rgb/parse.ts +302 -0
- package/src/color/rgb/serialize.ts +144 -0
- package/src/color/types.ts +57 -0
- package/src/color/xyz/analyze.ts +80 -0
- package/src/color/xyz/construct.ts +19 -0
- package/src/color/xyz/convert.ts +71 -0
- package/src/color/xyz/index.ts +3 -0
- package/src/color/xyz/internal.ts +23 -0
- package/src/credential/README.md +107 -0
- package/src/credential/api-key.ts +158 -0
- package/src/credential/bearer.ts +73 -0
- package/src/credential/index.ts +4 -0
- package/src/credential/json-web-token.ts +96 -0
- package/src/credential/password.ts +170 -0
- package/src/cron/README.md +86 -0
- package/src/cron/cron.ts +87 -0
- package/src/cron/index.ts +1 -0
- package/src/css/README.md +93 -0
- package/src/css/class.ts +559 -0
- package/src/css/index.ts +1 -0
- package/src/drizzle/README.md +1 -0
- package/src/drizzle/drizzle.ts +1 -0
- package/src/drizzle/helper.ts +47 -0
- package/src/drizzle/index.ts +5 -0
- package/src/drizzle/infer.ts +52 -0
- package/src/drizzle/kysely.ts +8 -0
- package/src/drizzle/pagination.ts +200 -0
- package/src/email/README.md +1 -0
- package/src/email/index.ts +1 -0
- package/src/email/resend.ts +25 -0
- package/src/encoding/README.md +66 -79
- package/src/encoding/base64.ts +13 -4
- package/src/environment/README.md +97 -0
- package/src/environment/basic.ts +26 -0
- package/src/environment/device.ts +311 -0
- package/src/environment/feature.ts +285 -0
- package/src/environment/geo.ts +337 -0
- package/src/environment/index.ts +7 -0
- package/src/environment/runtime.ts +400 -0
- package/src/environment/snapshot.ts +60 -0
- package/src/environment/variable.ts +239 -0
- package/src/event/README.md +90 -0
- package/src/event/class-event-proxy.ts +229 -0
- package/src/event/common.ts +29 -0
- package/src/event/event-manager.ts +203 -0
- package/src/event/index.ts +4 -0
- package/src/event/instance-event-proxy.ts +187 -0
- package/src/event/internal.ts +24 -0
- package/src/exception/README.md +96 -0
- package/src/exception/browser.ts +219 -0
- package/src/exception/index.ts +4 -0
- package/src/exception/nodejs.ts +169 -0
- package/src/exception/normalize.ts +106 -0
- package/src/exception/types.ts +99 -0
- package/src/form/README.md +25 -0
- package/src/form/index.ts +1 -0
- package/src/form/inputor-controller/base.ts +874 -0
- package/src/form/inputor-controller/boolean.ts +39 -0
- package/src/form/inputor-controller/file.ts +39 -0
- package/src/form/inputor-controller/form.ts +181 -0
- package/src/form/inputor-controller/helper.ts +117 -0
- package/src/form/inputor-controller/index.ts +17 -0
- package/src/form/inputor-controller/multi-select.ts +99 -0
- package/src/form/inputor-controller/number.ts +116 -0
- package/src/form/inputor-controller/select.ts +109 -0
- package/src/form/inputor-controller/text.ts +82 -0
- package/src/http/READMD.md +1 -0
- package/src/http/api/api-core.ts +84 -0
- package/src/http/api/api-handler.ts +79 -0
- package/src/http/api/api-host.ts +47 -0
- package/src/http/api/api-result.ts +56 -0
- package/src/http/api/api-schema.ts +154 -0
- package/src/http/api/api-server.ts +130 -0
- package/src/http/api/api-test.ts +142 -0
- package/src/http/api/api-type.ts +37 -0
- package/src/http/api/api.ts +81 -0
- package/src/http/api/index.ts +11 -0
- package/src/http/api-adapter/api-core-node-http.ts +260 -0
- package/src/http/api-adapter/api-host-node-http.ts +156 -0
- package/src/http/api-adapter/api-result-arktype.ts +297 -0
- package/src/http/api-adapter/api-result-zod.ts +286 -0
- package/src/http/api-adapter/index.ts +5 -0
- package/src/http/bin/gen-api-list/gen-api-list.ts +126 -0
- package/src/http/bin/gen-api-list/index.ts +1 -0
- package/src/http/bin/gen-api-test/gen-api-test.ts +136 -0
- package/src/http/bin/gen-api-test/index.ts +1 -0
- package/src/http/bin/gen-api-type/calc-code.ts +25 -0
- package/src/http/bin/gen-api-type/gen-api-type.ts +127 -0
- package/src/http/bin/gen-api-type/index.ts +2 -0
- package/src/http/bin/index.ts +2 -0
- package/src/http/index.ts +3 -0
- package/src/huawei/README.md +1 -0
- package/src/huawei/index.ts +2 -0
- package/src/huawei/moderation/index.ts +1 -0
- package/src/huawei/moderation/moderation.ts +355 -0
- package/src/huawei/obs/esdk-obs-nodejs.d.ts +87 -0
- package/src/huawei/obs/index.ts +1 -0
- package/src/huawei/obs/obs.ts +42 -0
- package/src/identifier/README.md +92 -0
- package/src/identifier/id.ts +119 -0
- package/src/identifier/index.ts +2 -0
- package/src/identifier/uuid.ts +187 -0
- package/src/index.ts +33 -1
- package/src/json/README.md +92 -0
- package/src/json/index.ts +1 -0
- package/src/json/repair.ts +18 -0
- package/src/log/README.md +79 -0
- package/src/log/index.ts +5 -0
- package/src/log/log-emitter.ts +72 -0
- package/src/log/log-record.ts +10 -0
- package/src/log/log-scheduler.ts +74 -0
- package/src/log/log-type.ts +8 -0
- package/src/log/logger.ts +554 -0
- package/src/openai/README.md +1 -0
- package/src/openai/index.ts +1 -0
- package/src/openai/openai.ts +510 -0
- package/src/orchestration/README.md +91 -0
- package/src/orchestration/coordination/barrier.ts +214 -0
- package/src/orchestration/coordination/count-down-latch.ts +215 -0
- package/src/orchestration/coordination/errors.ts +98 -0
- package/src/orchestration/coordination/index.ts +16 -0
- package/src/orchestration/coordination/internal/wait-constraints.ts +95 -0
- package/src/orchestration/coordination/internal/wait-queue.ts +109 -0
- package/src/orchestration/coordination/keyed-lock.ts +168 -0
- package/src/orchestration/coordination/mutex.ts +257 -0
- package/src/orchestration/coordination/permit.ts +127 -0
- package/src/orchestration/coordination/read-write-lock.ts +444 -0
- package/src/orchestration/coordination/semaphore.ts +280 -0
- package/src/orchestration/dispatching/dispatcher.ts +83 -0
- package/src/orchestration/dispatching/index.ts +2 -0
- package/src/orchestration/dispatching/selector/base-selector.ts +39 -0
- package/src/orchestration/dispatching/selector/down-count-selector.ts +119 -0
- package/src/orchestration/dispatching/selector/index.ts +2 -0
- package/src/orchestration/index.ts +3 -0
- package/src/orchestration/scheduling/index.ts +2 -0
- package/src/orchestration/scheduling/scheduler.ts +103 -0
- package/src/orchestration/scheduling/task.ts +32 -0
- package/src/random/README.md +56 -86
- package/src/random/base.ts +66 -0
- package/src/random/index.ts +5 -1
- package/src/random/random-boolean.ts +40 -0
- package/src/random/random-integer.ts +60 -0
- package/src/random/random-number.ts +72 -0
- package/src/random/random-string.ts +66 -0
- package/src/reactor/README.md +4 -0
- package/src/reactor/reactor-core/primitive.ts +9 -9
- package/src/reactor/reactor-core/reactive-system.ts +5 -5
- package/src/request/README.md +108 -0
- package/src/request/fetch/base.ts +108 -0
- package/src/request/fetch/browser.ts +285 -0
- package/src/request/fetch/general.ts +20 -0
- package/src/request/fetch/index.ts +4 -0
- package/src/request/fetch/nodejs.ts +285 -0
- package/src/request/index.ts +2 -0
- package/src/request/request/base.ts +250 -0
- package/src/request/request/general.ts +64 -0
- package/src/request/request/index.ts +3 -0
- package/src/request/request/resource.ts +68 -0
- package/src/result/README.md +4 -0
- package/src/result/controller.ts +54 -0
- package/src/result/either.ts +193 -0
- package/src/result/index.ts +2 -0
- package/src/route/README.md +105 -0
- package/src/route/adapter/browser.ts +122 -0
- package/src/route/adapter/driver.ts +56 -0
- package/src/route/adapter/index.ts +2 -0
- package/src/route/index.ts +3 -0
- package/src/route/router/index.ts +2 -0
- package/src/route/router/route.ts +630 -0
- package/src/route/router/router.ts +1642 -0
- package/src/route/uri/hash.ts +308 -0
- package/src/route/uri/index.ts +7 -0
- package/src/route/uri/pathname.ts +376 -0
- package/src/route/uri/search.ts +413 -0
- package/src/singleton/README.md +79 -0
- package/src/singleton/factory.ts +55 -0
- package/src/singleton/index.ts +2 -0
- package/src/singleton/manager.ts +204 -0
- package/src/socket/README.md +105 -0
- package/src/socket/client/index.ts +2 -0
- package/src/socket/client/socket-unit.ts +660 -0
- package/src/socket/client/socket.ts +203 -0
- package/src/socket/common/index.ts +2 -0
- package/src/socket/common/socket-unit-common.ts +23 -0
- package/src/socket/common/socket-unit-heartbeat.ts +427 -0
- package/src/socket/index.ts +3 -0
- package/src/socket/server/index.ts +3 -0
- package/src/socket/server/server.ts +183 -0
- package/src/socket/server/socket-unit.ts +449 -0
- package/src/socket/server/socket.ts +264 -0
- package/src/storage/README.md +107 -0
- package/src/storage/index.ts +1 -0
- package/src/storage/table.ts +449 -0
- package/src/timer/README.md +86 -0
- package/src/timer/expiration/expiration-manager.ts +594 -0
- package/src/timer/expiration/index.ts +3 -0
- package/src/timer/expiration/min-heap.ts +208 -0
- package/src/timer/expiration/remaining-manager.ts +241 -0
- package/src/timer/index.ts +1 -0
- package/src/tube/README.md +99 -0
- package/src/tube/helper.ts +138 -0
- package/src/tube/index.ts +2 -0
- package/src/tube/tube.ts +880 -0
- package/src/type/README.md +54 -307
- package/src/type/class.ts +2 -2
- package/src/type/index.ts +14 -14
- package/src/type/is.ts +265 -2
- package/src/type/object.ts +37 -0
- package/src/type/string.ts +7 -2
- package/src/type/tuple.ts +6 -6
- package/src/type/union.ts +16 -0
- package/src/web/README.md +77 -0
- package/src/web/capture.ts +35 -0
- package/src/web/clipboard.ts +97 -0
- package/src/web/dom.ts +117 -0
- package/src/web/download.ts +16 -0
- package/src/web/event.ts +46 -0
- package/src/web/index.ts +10 -0
- package/src/web/local-storage.ts +113 -0
- package/src/web/location.ts +28 -0
- package/src/web/permission.ts +172 -0
- package/src/web/script-loader.ts +432 -0
- package/src/weixin/README.md +1 -0
- package/src/weixin/index.ts +2 -0
- package/src/weixin/official-account/authorization.ts +159 -0
- package/src/weixin/official-account/index.ts +2 -0
- package/src/weixin/official-account/js-api.ts +134 -0
- package/src/weixin/open/index.ts +1 -0
- package/src/weixin/open/oauth2.ts +133 -0
- package/tests/unit/abort/abort-manager.spec.ts +225 -0
- package/tests/unit/abort/abort-signal-listener-manager.spec.ts +62 -0
- package/tests/unit/ai/ai.spec.ts +85 -0
- package/tests/unit/aio/content.spec.ts +105 -0
- package/tests/unit/aio/json.spec.ts +147 -0
- package/tests/unit/aio/prompt.spec.ts +111 -0
- package/tests/unit/basic/array.spec.ts +1 -1
- package/tests/unit/basic/error.spec.ts +16 -4
- package/tests/unit/basic/schedule.spec.ts +74 -0
- package/tests/unit/basic/stream.spec.ts +91 -38
- package/tests/unit/basic/string.spec.ts +0 -9
- package/tests/unit/color/rgb/analyze.spec.ts +110 -0
- package/tests/unit/color/rgb/construct.spec.ts +56 -0
- package/tests/unit/color/rgb/convert.spec.ts +60 -0
- package/tests/unit/color/rgb/derive.spec.ts +103 -0
- package/tests/unit/color/rgb/parse.spec.ts +66 -0
- package/tests/unit/color/rgb/serialize.spec.ts +46 -0
- package/tests/unit/color/xyz/analyze.spec.ts +33 -0
- package/tests/unit/color/xyz/construct.spec.ts +10 -0
- package/tests/unit/color/xyz/convert.spec.ts +18 -0
- package/tests/unit/credential/api-key.spec.ts +37 -0
- package/tests/unit/credential/bearer.spec.ts +23 -0
- package/tests/unit/credential/json-web-token.spec.ts +23 -0
- package/tests/unit/credential/password.spec.ts +41 -0
- package/tests/unit/cron/cron.spec.ts +84 -0
- package/tests/unit/css/class.spec.ts +157 -0
- package/tests/unit/environment/basic.spec.ts +20 -0
- package/tests/unit/environment/device.spec.ts +146 -0
- package/tests/unit/environment/feature.spec.ts +388 -0
- package/tests/unit/environment/geo.spec.ts +111 -0
- package/tests/unit/environment/runtime.spec.ts +364 -0
- package/tests/unit/environment/snapshot.spec.ts +4 -0
- package/tests/unit/environment/variable.spec.ts +190 -0
- package/tests/unit/event/class-event-proxy.spec.ts +225 -0
- package/tests/unit/event/event-manager.spec.ts +246 -0
- package/tests/unit/event/instance-event-proxy.spec.ts +187 -0
- package/tests/unit/exception/browser.spec.ts +213 -0
- package/tests/unit/exception/nodejs.spec.ts +144 -0
- package/tests/unit/exception/normalize.spec.ts +57 -0
- package/tests/unit/form/inputor-controller/base.spec.ts +458 -0
- package/tests/unit/form/inputor-controller/boolean.spec.ts +30 -0
- package/tests/unit/form/inputor-controller/file.spec.ts +27 -0
- package/tests/unit/form/inputor-controller/form.spec.ts +120 -0
- package/tests/unit/form/inputor-controller/helper.spec.ts +67 -0
- package/tests/unit/form/inputor-controller/multi-select.spec.ts +34 -0
- package/tests/unit/form/inputor-controller/number.spec.ts +36 -0
- package/tests/unit/form/inputor-controller/select.spec.ts +49 -0
- package/tests/unit/form/inputor-controller/text.spec.ts +34 -0
- package/tests/unit/http/api/api-core-host.spec.ts +207 -0
- package/tests/unit/http/api/api-schema.spec.ts +120 -0
- package/tests/unit/http/api/api-server.spec.ts +363 -0
- package/tests/unit/http/api/api-test.spec.ts +117 -0
- package/tests/unit/http/api/api.spec.ts +121 -0
- package/tests/unit/http/api-adapter/node-http.spec.ts +191 -0
- package/tests/unit/identifier/id.spec.ts +71 -0
- package/tests/unit/identifier/uuid.spec.ts +85 -0
- package/tests/unit/json/repair.spec.ts +11 -0
- package/tests/unit/log/log-emitter.spec.ts +33 -0
- package/tests/unit/log/log-scheduler.spec.ts +40 -0
- package/tests/unit/log/log-type.spec.ts +7 -0
- package/tests/unit/log/logger.spec.ts +237 -0
- package/tests/unit/openai/openai.spec.ts +64 -0
- package/tests/unit/orchestration/coordination/barrier.spec.ts +96 -0
- package/tests/unit/orchestration/coordination/count-down-latch.spec.ts +63 -0
- package/tests/unit/orchestration/coordination/errors.spec.ts +29 -0
- package/tests/unit/orchestration/coordination/keyed-lock.spec.ts +109 -0
- package/tests/unit/orchestration/coordination/mutex.spec.ts +132 -0
- package/tests/unit/orchestration/coordination/permit.spec.ts +43 -0
- package/tests/unit/orchestration/coordination/read-write-lock.spec.ts +154 -0
- package/tests/unit/orchestration/coordination/semaphore.spec.ts +135 -0
- package/tests/unit/orchestration/dispatching/dispatcher.spec.ts +41 -0
- package/tests/unit/orchestration/dispatching/selector/down-count-selector.spec.ts +81 -0
- package/tests/unit/orchestration/scheduling/scheduler.spec.ts +103 -0
- package/tests/unit/random/base.spec.ts +58 -0
- package/tests/unit/random/random-boolean.spec.ts +25 -0
- package/tests/unit/random/random-integer.spec.ts +32 -0
- package/tests/unit/random/random-number.spec.ts +33 -0
- package/tests/unit/random/random-string.spec.ts +22 -0
- package/tests/unit/reactor/alien-signals-effect.spec.ts +11 -10
- package/tests/unit/reactor/preact-signal.spec.ts +1 -2
- package/tests/unit/request/fetch/browser.spec.ts +222 -0
- package/tests/unit/request/fetch/general.spec.ts +43 -0
- package/tests/unit/request/fetch/nodejs.spec.ts +225 -0
- package/tests/unit/request/request/base.spec.ts +385 -0
- package/tests/unit/request/request/general.spec.ts +161 -0
- package/tests/unit/route/router/route.spec.ts +431 -0
- package/tests/unit/route/router/router.spec.ts +407 -0
- package/tests/unit/route/uri/hash.spec.ts +72 -0
- package/tests/unit/route/uri/pathname.spec.ts +147 -0
- package/tests/unit/route/uri/search.spec.ts +107 -0
- package/tests/unit/singleton/singleton.spec.ts +49 -0
- package/tests/unit/socket/client.spec.ts +208 -0
- package/tests/unit/socket/server.spec.ts +135 -0
- package/tests/unit/socket/socket-unit-heartbeat.spec.ts +214 -0
- package/tests/unit/storage/table.spec.ts +620 -0
- package/tests/unit/timer/expiration/expiration-manager.spec.ts +464 -0
- package/tests/unit/timer/expiration/min-heap.spec.ts +71 -0
- package/tests/unit/timer/expiration/remaining-manager.spec.ts +234 -0
- package/tests/unit/tube/helper.spec.ts +139 -0
- package/tests/unit/tube/tube.spec.ts +501 -0
- package/.oxlintrc.json +0 -5
- package/src/random/uuid.ts +0 -103
- package/tests/unit/random/uuid.spec.ts +0 -37
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { afterEach, expect, test, vi } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { CoordinationAbortError, CoordinationTimeoutError, Semaphore } from "#Source/orchestration/index.ts"
|
|
4
|
+
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
vi.clearAllTimers()
|
|
7
|
+
vi.useRealTimers()
|
|
8
|
+
vi.restoreAllMocks()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
test("Semaphore tracks capacity and grants permits in FIFO order", async () => {
|
|
12
|
+
const semaphore = new Semaphore(2)
|
|
13
|
+
|
|
14
|
+
expect(semaphore.getMaxConcurrency()).toBe(2)
|
|
15
|
+
expect(semaphore.getActiveCount()).toBe(0)
|
|
16
|
+
expect(semaphore.getAvailableCount()).toBe(2)
|
|
17
|
+
expect(semaphore.isSaturated()).toBe(false)
|
|
18
|
+
|
|
19
|
+
const firstPermit = semaphore.tryAcquire()
|
|
20
|
+
const secondPermit = semaphore.tryAcquire()
|
|
21
|
+
|
|
22
|
+
expect(firstPermit).toBeDefined()
|
|
23
|
+
expect(secondPermit).toBeDefined()
|
|
24
|
+
expect(semaphore.isSaturated()).toBe(true)
|
|
25
|
+
expect(semaphore.getActiveCount()).toBe(2)
|
|
26
|
+
expect(semaphore.getAvailableCount()).toBe(0)
|
|
27
|
+
|
|
28
|
+
const thirdPermitPromise = semaphore.acquire()
|
|
29
|
+
const fourthPermitPromise = semaphore.acquire()
|
|
30
|
+
|
|
31
|
+
expect(semaphore.getPendingCount()).toBe(2)
|
|
32
|
+
expect(semaphore.tryAcquire()).toBeUndefined()
|
|
33
|
+
|
|
34
|
+
firstPermit?.release()
|
|
35
|
+
|
|
36
|
+
const thirdPermit = await thirdPermitPromise
|
|
37
|
+
let fourthResolved = false
|
|
38
|
+
void fourthPermitPromise.then(() => {
|
|
39
|
+
fourthResolved = true
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
await Promise.resolve()
|
|
43
|
+
|
|
44
|
+
expect(semaphore.getPendingCount()).toBe(1)
|
|
45
|
+
expect(fourthResolved).toBe(false)
|
|
46
|
+
|
|
47
|
+
secondPermit?.release()
|
|
48
|
+
const fourthPermit = await fourthPermitPromise
|
|
49
|
+
|
|
50
|
+
expect(semaphore.getPendingCount()).toBe(0)
|
|
51
|
+
|
|
52
|
+
thirdPermit.release()
|
|
53
|
+
fourthPermit.release()
|
|
54
|
+
|
|
55
|
+
expect(semaphore.getActiveCount()).toBe(0)
|
|
56
|
+
expect(semaphore.getAvailableCount()).toBe(2)
|
|
57
|
+
expect(semaphore.isSaturated()).toBe(false)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test("Semaphore acquire rejects on timeout and abort without leaking queue entries", async () => {
|
|
61
|
+
vi.useFakeTimers()
|
|
62
|
+
|
|
63
|
+
const semaphore = new Semaphore(1)
|
|
64
|
+
const blockingPermit = semaphore.tryAcquire()
|
|
65
|
+
|
|
66
|
+
expect(blockingPermit).toBeDefined()
|
|
67
|
+
|
|
68
|
+
const timeoutPromise = semaphore.acquire({ timeout: 15 })
|
|
69
|
+
|
|
70
|
+
expect(semaphore.getPendingCount()).toBe(1)
|
|
71
|
+
|
|
72
|
+
await vi.advanceTimersByTimeAsync(15)
|
|
73
|
+
|
|
74
|
+
await expect(timeoutPromise).rejects.toBeInstanceOf(CoordinationTimeoutError)
|
|
75
|
+
expect(semaphore.getPendingCount()).toBe(0)
|
|
76
|
+
|
|
77
|
+
const abortController = new AbortController()
|
|
78
|
+
const abortPromise = semaphore.acquire({ abortSignal: abortController.signal })
|
|
79
|
+
|
|
80
|
+
expect(semaphore.getPendingCount()).toBe(1)
|
|
81
|
+
|
|
82
|
+
abortController.abort("stopped")
|
|
83
|
+
|
|
84
|
+
await expect(abortPromise).rejects.toBeInstanceOf(CoordinationAbortError)
|
|
85
|
+
expect(semaphore.getPendingCount()).toBe(0)
|
|
86
|
+
|
|
87
|
+
blockingPermit?.release()
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test("Semaphore runExclusive releases capacity after callback failure", async () => {
|
|
91
|
+
const semaphore = new Semaphore(1)
|
|
92
|
+
const error = new Error("boom")
|
|
93
|
+
|
|
94
|
+
await expect(semaphore.runExclusive(() => 1)).resolves.toBe(1)
|
|
95
|
+
expect(semaphore.getActiveCount()).toBe(0)
|
|
96
|
+
|
|
97
|
+
await expect(semaphore.runExclusive(() => {
|
|
98
|
+
throw error
|
|
99
|
+
})).rejects.toThrow(error)
|
|
100
|
+
|
|
101
|
+
expect(semaphore.getActiveCount()).toBe(0)
|
|
102
|
+
expect(semaphore.isSaturated()).toBe(false)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test("Semaphore duplicate release stays silent by default and uses custom handler when provided", () => {
|
|
106
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {
|
|
107
|
+
// no-op to silence warnings during test
|
|
108
|
+
})
|
|
109
|
+
const silentSemaphore = new Semaphore(1)
|
|
110
|
+
const silentPermit = silentSemaphore.tryAcquire()
|
|
111
|
+
|
|
112
|
+
expect(silentPermit).toBeDefined()
|
|
113
|
+
|
|
114
|
+
silentPermit?.release()
|
|
115
|
+
silentPermit?.release()
|
|
116
|
+
silentPermit?.release()
|
|
117
|
+
|
|
118
|
+
expect(silentSemaphore.getActiveCount()).toBe(0)
|
|
119
|
+
expect(warnSpy).not.toHaveBeenCalled()
|
|
120
|
+
|
|
121
|
+
const onDuplicateRelease = vi.fn()
|
|
122
|
+
const customSemaphore = new Semaphore(1, { onDuplicateRelease })
|
|
123
|
+
const customPermit = customSemaphore.tryAcquire()
|
|
124
|
+
|
|
125
|
+
expect(customPermit).toBeDefined()
|
|
126
|
+
|
|
127
|
+
customPermit?.release()
|
|
128
|
+
customPermit?.release()
|
|
129
|
+
customPermit?.release()
|
|
130
|
+
|
|
131
|
+
expect(customSemaphore.getActiveCount()).toBe(0)
|
|
132
|
+
expect(onDuplicateRelease).toHaveBeenCalledTimes(2)
|
|
133
|
+
expect(onDuplicateRelease).toHaveBeenCalledWith("Semaphore permit release was called more than once.")
|
|
134
|
+
expect(warnSpy).not.toHaveBeenCalled()
|
|
135
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { expect, test } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { Dispatcher, DownCountSelector } from "#Source/orchestration/index.ts"
|
|
4
|
+
|
|
5
|
+
test("Dispatcher getSelector reuses selectors by id and applies filter on first creation", async () => {
|
|
6
|
+
const dispatcher = new Dispatcher({
|
|
7
|
+
itemList: ["alpha", "beta", "gamma"],
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
const firstResult = dispatcher.getSelector({
|
|
11
|
+
id: "shared",
|
|
12
|
+
filter: (item) => item !== "alpha",
|
|
13
|
+
})
|
|
14
|
+
const secondResult = dispatcher.getSelector({
|
|
15
|
+
id: "shared",
|
|
16
|
+
filter: () => true,
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
expect(firstResult.selector).toBeInstanceOf(DownCountSelector)
|
|
20
|
+
expect(secondResult.selector).toBe(firstResult.selector)
|
|
21
|
+
|
|
22
|
+
const firstItem = await firstResult.selector.getItem()
|
|
23
|
+
|
|
24
|
+
expect(firstItem.isRight()).toBe(true)
|
|
25
|
+
expect(firstItem.assertRight().getRight().item).toBe("beta")
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test("Dispatcher destroySelector discards an existing selector and allows recreation", () => {
|
|
29
|
+
const dispatcher = new Dispatcher({
|
|
30
|
+
itemList: ["alpha", "beta"],
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const firstSelector = dispatcher.getSelector({ id: "shared" }).selector
|
|
34
|
+
|
|
35
|
+
dispatcher.destroySelector("missing")
|
|
36
|
+
dispatcher.destroySelector("shared")
|
|
37
|
+
|
|
38
|
+
const recreatedSelector = dispatcher.getSelector({ id: "shared" }).selector
|
|
39
|
+
|
|
40
|
+
expect(recreatedSelector).not.toBe(firstSelector)
|
|
41
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { expect, test } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { DownCountSelector } from "#Source/orchestration/index.ts"
|
|
4
|
+
|
|
5
|
+
test("DownCountSelector getId returns the configured selector id", () => {
|
|
6
|
+
const selector = new DownCountSelector({
|
|
7
|
+
id: "selector-1",
|
|
8
|
+
itemList: ["alpha"],
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
expect(selector.getId()).toBe("selector-1")
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test("DownCountSelector getItem distinguishes no choice, no available, and reset-driven reuse", async () => {
|
|
15
|
+
const emptySelector = new DownCountSelector<string>({
|
|
16
|
+
id: "empty",
|
|
17
|
+
itemList: [],
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const emptyResult = await emptySelector.getItem()
|
|
21
|
+
|
|
22
|
+
expect(emptyResult.isLeft()).toBe(true)
|
|
23
|
+
expect(emptyResult.assertLeft().getLeft()).toEqual({
|
|
24
|
+
code: "NO_CHOICE",
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const noAvailableSelector = new DownCountSelector({
|
|
28
|
+
id: "no-available",
|
|
29
|
+
itemList: ["alpha", "beta"],
|
|
30
|
+
resetAllWhenNoAvailable: false,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const firstResult = await noAvailableSelector.getItem()
|
|
34
|
+
|
|
35
|
+
expect(firstResult.isRight()).toBe(true)
|
|
36
|
+
|
|
37
|
+
firstResult.assertRight().getRight().markUnavailable()
|
|
38
|
+
|
|
39
|
+
const secondResult = await noAvailableSelector.getItem()
|
|
40
|
+
|
|
41
|
+
expect(secondResult.isRight()).toBe(true)
|
|
42
|
+
expect(secondResult.assertRight().getRight().item).toBe("beta")
|
|
43
|
+
|
|
44
|
+
secondResult.assertRight().getRight().markUnavailable()
|
|
45
|
+
|
|
46
|
+
const noAvailableResult = await noAvailableSelector.getItem()
|
|
47
|
+
|
|
48
|
+
expect(noAvailableResult.isLeft()).toBe(true)
|
|
49
|
+
expect(noAvailableResult.assertLeft().getLeft()).toEqual({
|
|
50
|
+
code: "NO_AVAILABLE",
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const resetSelector = new DownCountSelector({
|
|
54
|
+
id: "reset",
|
|
55
|
+
itemList: ["only"],
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const resetFirstResult = await resetSelector.getItem()
|
|
59
|
+
|
|
60
|
+
expect(resetFirstResult.isRight()).toBe(true)
|
|
61
|
+
|
|
62
|
+
resetFirstResult.assertRight().getRight().markUnavailable()
|
|
63
|
+
|
|
64
|
+
const resetSecondResult = await resetSelector.getItem()
|
|
65
|
+
|
|
66
|
+
expect(resetSecondResult.isRight()).toBe(true)
|
|
67
|
+
expect(resetSecondResult.assertRight().getRight().item).toBe("only")
|
|
68
|
+
|
|
69
|
+
resetSecondResult.assertRight().getRight().markUnavailable()
|
|
70
|
+
|
|
71
|
+
const recoveredResult = await resetSelector.getItem()
|
|
72
|
+
|
|
73
|
+
expect(recoveredResult.isRight()).toBe(true)
|
|
74
|
+
|
|
75
|
+
recoveredResult.assertRight().getRight().markAvailable()
|
|
76
|
+
|
|
77
|
+
const availableAgainResult = await resetSelector.getItem()
|
|
78
|
+
|
|
79
|
+
expect(availableAgainResult.isRight()).toBe(true)
|
|
80
|
+
expect(availableAgainResult.assertRight().getRight().item).toBe("only")
|
|
81
|
+
})
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { expect, test, vi } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { Scheduler, Task } from "#Source/orchestration/index.ts"
|
|
4
|
+
|
|
5
|
+
test("Scheduler runs one-shot tasks and records run logs", async () => {
|
|
6
|
+
vi.useFakeTimers()
|
|
7
|
+
vi.setSystemTime(new Date(2_026, 0, 1, 0, 0, 0, 0))
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const run = vi.fn(() => undefined)
|
|
11
|
+
const runAt = new Date(Date.now() + 1_000)
|
|
12
|
+
const scheduler = new Scheduler({
|
|
13
|
+
tasks: [
|
|
14
|
+
new Task({
|
|
15
|
+
name: "alpha",
|
|
16
|
+
cron: runAt,
|
|
17
|
+
run,
|
|
18
|
+
}),
|
|
19
|
+
],
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
scheduler.run()
|
|
23
|
+
|
|
24
|
+
await vi.advanceTimersByTimeAsync(999)
|
|
25
|
+
expect(run).toHaveBeenCalledTimes(0)
|
|
26
|
+
|
|
27
|
+
await vi.advanceTimersByTimeAsync(1)
|
|
28
|
+
|
|
29
|
+
expect(run).toHaveBeenCalledTimes(1)
|
|
30
|
+
expect(scheduler.getErrLog()).toEqual([])
|
|
31
|
+
expect(scheduler.getRunLog()).toHaveLength(1)
|
|
32
|
+
expect(scheduler.getRunLog()[0]).toEqual({
|
|
33
|
+
name: "alpha",
|
|
34
|
+
time: runAt,
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
vi.clearAllTimers()
|
|
39
|
+
vi.useRealTimers()
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test("Scheduler records both run and error logs when a task fails", async () => {
|
|
44
|
+
vi.useFakeTimers()
|
|
45
|
+
vi.setSystemTime(new Date(2_026, 0, 1, 0, 0, 0, 0))
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const error = new Error("boom")
|
|
49
|
+
const run = vi.fn(async () => await Promise.reject(error))
|
|
50
|
+
const runAt = new Date(Date.now() + 1_000)
|
|
51
|
+
const scheduler = new Scheduler({
|
|
52
|
+
tasks: [
|
|
53
|
+
new Task({
|
|
54
|
+
name: "beta",
|
|
55
|
+
cron: runAt,
|
|
56
|
+
run,
|
|
57
|
+
}),
|
|
58
|
+
],
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
scheduler.run()
|
|
62
|
+
await vi.advanceTimersByTimeAsync(1_000)
|
|
63
|
+
|
|
64
|
+
expect(run).toHaveBeenCalledTimes(1)
|
|
65
|
+
expect(scheduler.getRunLog()).toHaveLength(1)
|
|
66
|
+
expect(scheduler.getErrLog()).toEqual([
|
|
67
|
+
{
|
|
68
|
+
name: "beta",
|
|
69
|
+
time: runAt,
|
|
70
|
+
reason: "Error: boom",
|
|
71
|
+
},
|
|
72
|
+
])
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
vi.clearAllTimers()
|
|
76
|
+
vi.useRealTimers()
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test("Scheduler.run rejects duplicate starts", () => {
|
|
81
|
+
vi.useFakeTimers()
|
|
82
|
+
vi.setSystemTime(new Date(2_026, 0, 1, 0, 0, 0, 0))
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const scheduler = new Scheduler({
|
|
86
|
+
tasks: [
|
|
87
|
+
new Task({
|
|
88
|
+
name: "gamma",
|
|
89
|
+
cron: new Date(Date.now() + 1_000),
|
|
90
|
+
run: () => undefined,
|
|
91
|
+
}),
|
|
92
|
+
],
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
scheduler.run()
|
|
96
|
+
|
|
97
|
+
expect(() => scheduler.run()).toThrow("Scheduler is already running")
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
vi.clearAllTimers()
|
|
101
|
+
vi.useRealTimers()
|
|
102
|
+
}
|
|
103
|
+
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { expect, test, vi } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { getRandomIndex, getRandomUint32, getRandomUnitValue } from "#Source/random/index.ts"
|
|
4
|
+
|
|
5
|
+
test("getRandomUint32 returns an integer within uint32 range across available random sources", () => {
|
|
6
|
+
const cryptoBackedValue = getRandomUint32()
|
|
7
|
+
expect(Number.isInteger(cryptoBackedValue)).toBe(true)
|
|
8
|
+
expect(cryptoBackedValue).toBeGreaterThanOrEqual(0)
|
|
9
|
+
expect(cryptoBackedValue).toBeLessThan(0x1_0000_0000)
|
|
10
|
+
|
|
11
|
+
vi.stubGlobal("crypto", undefined)
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const fallbackValue = getRandomUint32()
|
|
15
|
+
expect(Number.isInteger(fallbackValue)).toBe(true)
|
|
16
|
+
expect(fallbackValue).toBeGreaterThanOrEqual(0)
|
|
17
|
+
expect(fallbackValue).toBeLessThan(0x1_0000_0000)
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
vi.unstubAllGlobals()
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test("getRandomUnitValue returns a unit-interval float across available random sources", () => {
|
|
25
|
+
const cryptoBackedValue = getRandomUnitValue()
|
|
26
|
+
expect(cryptoBackedValue).toBeGreaterThanOrEqual(0)
|
|
27
|
+
expect(cryptoBackedValue).toBeLessThan(1)
|
|
28
|
+
|
|
29
|
+
vi.stubGlobal("crypto", undefined)
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const fallbackValue = getRandomUnitValue()
|
|
33
|
+
expect(fallbackValue).toBeGreaterThanOrEqual(0)
|
|
34
|
+
expect(fallbackValue).toBeLessThan(1)
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
vi.unstubAllGlobals()
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test("getRandomIndex returns an integer index within alphabet bounds across available random sources", () => {
|
|
42
|
+
const cryptoBackedValue = getRandomIndex(3)
|
|
43
|
+
expect(Number.isInteger(cryptoBackedValue)).toBe(true)
|
|
44
|
+
expect(cryptoBackedValue).toBeGreaterThanOrEqual(0)
|
|
45
|
+
expect(cryptoBackedValue).toBeLessThan(3)
|
|
46
|
+
|
|
47
|
+
vi.stubGlobal("crypto", undefined)
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const fallbackValue = getRandomIndex(3)
|
|
51
|
+
expect(Number.isInteger(fallbackValue)).toBe(true)
|
|
52
|
+
expect(fallbackValue).toBeGreaterThanOrEqual(0)
|
|
53
|
+
expect(fallbackValue).toBeLessThan(3)
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
vi.unstubAllGlobals()
|
|
57
|
+
}
|
|
58
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { expect, test, vi } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { randomBoolean } from "#Source/random/index.ts"
|
|
4
|
+
|
|
5
|
+
test("randomBoolean returns expected output across probabilities and available random sources", () => {
|
|
6
|
+
const defaultValue = randomBoolean()
|
|
7
|
+
expect(typeof defaultValue).toBe("boolean")
|
|
8
|
+
|
|
9
|
+
expect(randomBoolean(0)).toBe(false)
|
|
10
|
+
expect(randomBoolean(1)).toBe(true)
|
|
11
|
+
expect(() => randomBoolean(Number.NaN)).toThrow(TypeError)
|
|
12
|
+
expect(() => randomBoolean(Number.POSITIVE_INFINITY)).toThrow(TypeError)
|
|
13
|
+
expect(() => randomBoolean(-0.1)).toThrow(RangeError)
|
|
14
|
+
expect(() => randomBoolean(1.1)).toThrow(RangeError)
|
|
15
|
+
|
|
16
|
+
vi.stubGlobal("crypto", undefined)
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const fallbackValue = randomBoolean(0.5)
|
|
20
|
+
expect(typeof fallbackValue).toBe("boolean")
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
vi.unstubAllGlobals()
|
|
24
|
+
}
|
|
25
|
+
})
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { expect, test, vi } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { randomInteger } from "#Source/random/index.ts"
|
|
4
|
+
|
|
5
|
+
test("randomInteger returns expected output across ranges and available random sources", () => {
|
|
6
|
+
const singleBoundValue = randomInteger(5)
|
|
7
|
+
expect(singleBoundValue).toBeGreaterThanOrEqual(0)
|
|
8
|
+
expect(singleBoundValue).toBeLessThanOrEqual(5)
|
|
9
|
+
expect(Number.isInteger(singleBoundValue)).toBe(true)
|
|
10
|
+
|
|
11
|
+
const reversedRangeValue = randomInteger(8, 3)
|
|
12
|
+
expect(reversedRangeValue).toBeGreaterThanOrEqual(3)
|
|
13
|
+
expect(reversedRangeValue).toBeLessThanOrEqual(8)
|
|
14
|
+
expect(Number.isInteger(reversedRangeValue)).toBe(true)
|
|
15
|
+
|
|
16
|
+
expect(randomInteger(4, 4)).toBe(4)
|
|
17
|
+
expect(() => randomInteger(1.5)).toThrow(TypeError)
|
|
18
|
+
expect(() => randomInteger(Number.NaN)).toThrow(TypeError)
|
|
19
|
+
expect(() => randomInteger(1, Number.POSITIVE_INFINITY)).toThrow(TypeError)
|
|
20
|
+
|
|
21
|
+
vi.stubGlobal("crypto", undefined)
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const fallbackValue = randomInteger(-2, 2)
|
|
25
|
+
expect(fallbackValue).toBeGreaterThanOrEqual(-2)
|
|
26
|
+
expect(fallbackValue).toBeLessThanOrEqual(2)
|
|
27
|
+
expect(Number.isInteger(fallbackValue)).toBe(true)
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
vi.unstubAllGlobals()
|
|
31
|
+
}
|
|
32
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { expect, test, vi } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { randomNumber } from "#Source/random/index.ts"
|
|
4
|
+
|
|
5
|
+
test("randomNumber returns expected output across ranges and available random sources", () => {
|
|
6
|
+
const defaultValue = randomNumber()
|
|
7
|
+
expect(defaultValue).toBeGreaterThanOrEqual(0)
|
|
8
|
+
expect(defaultValue).toBeLessThan(1)
|
|
9
|
+
|
|
10
|
+
const singleBoundValue = randomNumber(5)
|
|
11
|
+
expect(singleBoundValue).toBeGreaterThanOrEqual(0)
|
|
12
|
+
expect(singleBoundValue).toBeLessThan(5)
|
|
13
|
+
|
|
14
|
+
const reversedRangeValue = randomNumber(10, 3)
|
|
15
|
+
expect(reversedRangeValue).toBeGreaterThanOrEqual(3)
|
|
16
|
+
expect(reversedRangeValue).toBeLessThan(10)
|
|
17
|
+
|
|
18
|
+
expect(randomNumber(4, 4)).toBe(4)
|
|
19
|
+
expect(() => randomNumber(Number.NaN)).toThrow(TypeError)
|
|
20
|
+
expect(() => randomNumber(Number.POSITIVE_INFINITY)).toThrow(TypeError)
|
|
21
|
+
expect(() => randomNumber(1, Number.NEGATIVE_INFINITY)).toThrow(TypeError)
|
|
22
|
+
|
|
23
|
+
vi.stubGlobal("crypto", undefined)
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const fallbackValue = randomNumber(-2, 2)
|
|
27
|
+
expect(fallbackValue).toBeGreaterThanOrEqual(-2)
|
|
28
|
+
expect(fallbackValue).toBeLessThan(2)
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
vi.unstubAllGlobals()
|
|
32
|
+
}
|
|
33
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { expect, test, vi } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { randomString } from "#Source/random/index.ts"
|
|
4
|
+
|
|
5
|
+
test("randomString returns expected output across available random sources", () => {
|
|
6
|
+
const cryptoBackedValue = randomString(12)
|
|
7
|
+
expect(cryptoBackedValue).toHaveLength(12)
|
|
8
|
+
|
|
9
|
+
const constrainedValue = randomString(10, "ab")
|
|
10
|
+
expect(constrainedValue.split("").every(char => char === "a" || char === "b")).toBe(true)
|
|
11
|
+
|
|
12
|
+
vi.stubGlobal("crypto", undefined)
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const fallbackValue = randomString(10, "ab")
|
|
16
|
+
expect(fallbackValue).toHaveLength(10)
|
|
17
|
+
expect(fallbackValue.split("").every(char => char === "a" || char === "b")).toBe(true)
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
vi.unstubAllGlobals()
|
|
21
|
+
}
|
|
22
|
+
})
|
|
@@ -49,16 +49,17 @@ test('should not run untracked inner effect', () => {
|
|
|
49
49
|
let outerEffectTruthyBranchRunTimes = 0;
|
|
50
50
|
const a = signal(() => 3);
|
|
51
51
|
const b = computed(() => a.get() > 0);
|
|
52
|
-
|
|
52
|
+
// outer effect
|
|
53
|
+
effect(() => {
|
|
53
54
|
if (b.get() === true) {
|
|
54
55
|
outerEffectTruthyBranchRunTimes = outerEffectTruthyBranchRunTimes + 1;
|
|
55
|
-
|
|
56
|
+
effect(() => {
|
|
56
57
|
if (a.get() === 0) {
|
|
57
58
|
throw new Error("bad");
|
|
58
59
|
}
|
|
59
|
-
});
|
|
60
|
+
}, { name: "inner effect" });
|
|
60
61
|
}
|
|
61
|
-
});
|
|
62
|
+
}, { name: "outer effect" });
|
|
62
63
|
|
|
63
64
|
expect(outerEffectTruthyBranchRunTimes).toBe(1);
|
|
64
65
|
a.set(2);
|
|
@@ -226,7 +227,7 @@ test('should duplicate subscribers do not affect the notify order', () => {
|
|
|
226
227
|
const srcB = signal(() => 0, { name: "srcB" });
|
|
227
228
|
const order: string[] = [];
|
|
228
229
|
|
|
229
|
-
|
|
230
|
+
effect(() => {
|
|
230
231
|
order.push('a');
|
|
231
232
|
reactiveSystem.setNoActiveNodeAsSub();
|
|
232
233
|
const isOne = srcB.get() === 1;
|
|
@@ -237,7 +238,7 @@ test('should duplicate subscribers do not affect the notify order', () => {
|
|
|
237
238
|
srcB.get();
|
|
238
239
|
srcA.get();
|
|
239
240
|
}, { name: "effect-a" });
|
|
240
|
-
|
|
241
|
+
effect(() => {
|
|
241
242
|
order.push('b');
|
|
242
243
|
srcA.get();
|
|
243
244
|
}, { name: "effect-b" });
|
|
@@ -357,20 +358,20 @@ test('should not execute skipped effects from previous failed flush when updatin
|
|
|
357
358
|
|
|
358
359
|
let effect3Executed = false;
|
|
359
360
|
|
|
360
|
-
|
|
361
|
+
effect(() => {
|
|
361
362
|
a.get();
|
|
362
363
|
}, { name: "effect1" });
|
|
363
|
-
|
|
364
|
+
effect(() => {
|
|
364
365
|
if (a.get() === 2) {
|
|
365
366
|
throw new Error('Error in effect 2');
|
|
366
367
|
}
|
|
367
368
|
}, { name: "effect2" });
|
|
368
|
-
|
|
369
|
+
effect(() => {
|
|
369
370
|
a.get();
|
|
370
371
|
d.get();
|
|
371
372
|
effect3Executed = true;
|
|
372
373
|
}, { name: "effect3" });
|
|
373
|
-
|
|
374
|
+
effect(() => {
|
|
374
375
|
b.get();
|
|
375
376
|
}, { name: "effect4" });
|
|
376
377
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { describe, it, vi, expect } from "vitest";
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import { Signal, computed, effect, effectScope, endBatch, reactiveSystem, signal, startBatch } from '#Source/reactor/index.ts';
|
|
3
|
+
import { Signal, effect, signal } from "#Source/reactor/index.ts"
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* 这个测试文件应该与 https://github.com/preactjs/signals/blob/main/packages/core/test/signal.test.tsx 保持同步。
|