| 1 | import { assert, describe, it } from "@effect/vitest" |
| 2 | import { ConfigProvider, Effect, Option } from "effect" |
| 3 | import { RunnerAddress, ShardingConfig } from "effect/cluster" |
| 4 | |
| 5 | describe("ShardingConfig", () => { |
| 6 | it.effect("bounds runner residency and storage reads by default", () => |
| 7 | Effect.gen(function*() { |
| 8 | assert.strictEqual(ShardingConfig.defaults.maxResidentEntities, 10_000) |
| 9 | assert.strictEqual(ShardingConfig.defaults.unprocessedMessageBatchSize, 1024) |
| 10 | |
| 11 | const defaults = yield* ShardingConfig.config.parse(ConfigProvider.fromUnknown({})) |
| 12 | assert.strictEqual(defaults.maxResidentEntities, 10_000) |
| 13 | assert.strictEqual(defaults.unprocessedMessageBatchSize, 1024) |
| 14 | |
| 15 | // environment values are integers only |
| 16 | const fromEnv = yield* ShardingConfig.config.parse( |
| 17 | ConfigProvider.fromUnknown({ maxResidentEntities: "500", unprocessedMessageBatchSize: "128" }) |
| 18 | ) |
| 19 | assert.strictEqual(fromEnv.maxResidentEntities, 500) |
| 20 | assert.strictEqual(fromEnv.unprocessedMessageBatchSize, 128) |
| 21 | |
| 22 | const invalid = yield* ShardingConfig.config.parse( |
| 23 | ConfigProvider.fromUnknown({ maxResidentEntities: "unbounded" }) |
| 24 | ).pipe(Effect.flip) |
| 25 | assert.strictEqual(invalid._tag, "ConfigError") |
| 26 | |
| 27 | // "unbounded" is only available programmatically |
| 28 | const layer = ShardingConfig.layer({ maxResidentEntities: "unbounded" }) |
| 29 | const config = yield* Effect.provide(ShardingConfig.ShardingConfig, layer) |
| 30 | assert.strictEqual(config.maxResidentEntities, "unbounded") |
| 31 | })) |
| 32 | |
| 33 | it.effect("omits the optional listen address when its host is absent", () => |
| 34 | Effect.gen(function*() { |
| 35 | const defaults = yield* ShardingConfig.config.parse(ConfigProvider.fromUnknown({})) |
| 36 | assert.ok(Option.isNone(defaults.runnerListenAddress)) |
| 37 | |
| 38 | const withHost = yield* ShardingConfig.config.parse( |
| 39 | ConfigProvider.fromUnknown({ listenHost: "0.0.0.0" }) |
| 40 | ) |
| 41 | assert.deepStrictEqual( |
| 42 | Option.getOrThrow(withHost.runnerListenAddress), |
| 43 | RunnerAddress.make("0.0.0.0", 34431) |
| 44 | ) |
| 45 | |
| 46 | const missingHost = yield* ShardingConfig.config.parse( |
| 47 | ConfigProvider.fromUnknown({ listenPort: "8080" }) |
| 48 | ) |
| 49 | assert.deepStrictEqual(missingHost.runnerListenAddress, Option.none()) |
| 50 | |
| 51 | const invalidPort = yield* ShardingConfig.config.parse( |
| 52 | ConfigProvider.fromUnknown({ listenHost: "0.0.0.0", listenPort: "invalid" }) |
| 53 | ).pipe(Effect.flip) |
| 54 | assert.strictEqual( |
| 55 | invalidPort.cause.message, |
| 56 | `Expected a string representing a finite number |
| 57 | at ["listenPort"]` |
| 58 | ) |
| 59 | })) |
| 60 | |
| 61 | it.effect("rejects non-positive residency and batch limits", () => |
| 62 | Effect.gen(function*() { |
| 63 | for ( |
| 64 | const values of [ |
| 65 | { maxResidentEntities: "0" }, |
| 66 | { maxResidentEntities: "-1" }, |
| 67 | { unprocessedMessageBatchSize: "0" }, |
| 68 | { unprocessedMessageBatchSize: "-1" } |
| 69 | ] |
| 70 | ) { |
| 71 | const error = yield* ShardingConfig.config.parse(ConfigProvider.fromUnknown(values)).pipe(Effect.flip) |
| 72 | assert.strictEqual(error._tag, "ConfigError") |
| 73 | } |
| 74 | })) |
| 75 | }) |