// ets_tracing: off import * as T from "@effect-ts/core/Effect" import type { Clock } from "@effect-ts/core/Effect/Clock" import * as F from "@effect-ts/core/Effect/Fiber" import * as L from "@effect-ts/core/Effect/Layer" import * as P from "@effect-ts/core/Effect/Promise" import type * as Random from "@effect-ts/core/Effect/Random" import type { Has } from "@effect-ts/core/Has" import { identity, pipe } from "@effect-ts/system/Function" import type * as Annotations from "@effect-ts/system/Testing/Annotations" import * as FibersPerTest from "@effect-ts/system/Testing/FibersPerTest" import * as Live from "@effect-ts/system/Testing/Live" import * as TestClock from "@effect-ts/system/Testing/TestClock" import * as TE from "@effect-ts/system/Testing/TestEnvironment" class MainProvider { constructor( readonly allocate: T.Effect, readonly release: T.UIO, readonly provide: ( self: T.Effect ) => T.Effect ) {} } const finalizers: T.UIO[] = [] function unsafeMainProvider(self: L.Layer) { const promise = P.unsafeMake(F.None) return new MainProvider( T.gen(function* (_) { const x = yield* _( T.forkDaemon( pipe( T.accessM((_: R) => P.succeed(_)(promise)), T.zipRight(T.never), T.provideSomeLayer(self), T.catchAllCause((c) => P.halt_(promise, c)) ) ) ) finalizers.push(F.interrupt(x)) yield* _(P.await(promise)) return true }), T.suspend(() => { const all = finalizers.splice(0) return T.forEach_(all.reverse(), identity) }), (self) => T.chain_(P.await(promise), (env) => T.provide(env)(self)) ) } export type TestEnvironment = Has & Has & Has & Has & Has export class TestRuntime { constructor( readonly it: ( name: string, self: () => T.Effect ) => void, readonly layer: L.Layer, readonly provide: ( self: T.Effect ) => T.Effect ) {} } export function perTest( f: (_: T.Effect) => T.Effect ) { return (_: TestRuntime): TestRuntime => new TestRuntime((name, self) => _.it(name, () => f(self())), _.layer, _.provide) } export function runtime(): TestRuntime export function runtime( f: ( _: typeof TE.TestEnvironment, __: L.Layer ) => L.Layer ): TestRuntime export function runtime( f?: ( _: typeof TE.TestEnvironment, __: L.Layer ) => L.Layer ): TestRuntime { const { allocate, provide, release } = unsafeMainProvider( // @ts-expect-error (f || identity)(TE.TestEnvironment, L.fromRawEffect(T.succeed(T.defaultEnv))) ) beforeAll(() => T.runPromise(allocate), 60_000) afterAll(() => T.runPromise(release), 60_000) const it_ = (name: string, self: () => T.Effect) => { it(name, () => T.runPromise(provide(FibersPerTest.fibersPerTest(self())))) } return new TestRuntime( it_, L.fromRawEffect(provide(T.environment())), provide ) } export function shared(layer: L.Layer): L.Layer { const { allocate, provide, release } = unsafeMainProvider(layer) beforeAll(() => T.runPromise(allocate)) afterAll(() => T.runPromise(release)) return L.fromRawEffect(provide(T.environment())) } export function adjust(ms: number) { return T.accessServiceM(TestClock.TestClock)((_) => _.adjust(ms)) } export function setTime(ms: number) { return T.accessServiceM(TestClock.TestClock)((_) => _.setTime(ms)) } export const sleeps = T.accessServiceM(TestClock.TestClock)((_) => _.sleeps) export const saveClock = T.accessServiceM(TestClock.TestClock)((_) => _.save) export function live( effect: T.Effect ): T.Effect, E, A> { return T.accessServiceM(Live.Live)((_) => _.provide(effect)) }