# TestClock

In most cases, we want our unit tests to run as quickly as possible. Waiting for real time to pass can slow down our tests significantly. Effect provides a handy tool called `TestClock` that allows us to **control time during testing**. This means we can efficiently and predictably test code that involves time without having to wait for the actual time to pass.

## How TestClock Works

Imagine `TestClock` as a wall clock that only moves forward when we adjust it manually using the `TestClock.adjust` and `TestClock.setTime` functions. The clock time does not progress on its own.

When we adjust the clock time, any effects scheduled to run at or before that time will execute. This allows us to simulate time passage in tests without waiting for real time.

**Example** (Simulating a Timeout with TestClock)

```ts
import { Effect, Fiber, Option } from "effect"
import { TestClock } from "effect/testing"
import * as assert from "node:assert"

const test = Effect.gen(function* () {
  // Create a fiber that sleeps for 5 minutes and then times out
  // after 1 minute
  const fiber = yield* Effect.sleep("5 minutes").pipe(
    Effect.map(Option.some),
    Effect.timeoutOrElse({
      duration: "1 minute",
      orElse: () => Effect.succeed(Option.none<void>()),
    }),
    Effect.forkChild,
  )

  // Adjust the TestClock by 1 minute to simulate the passage of time
  yield* TestClock.adjust("1 minute")

  // Get the result of the fiber
  const result = yield* Fiber.join(fiber)

  // Check if the result is None, indicating a timeout
  assert.ok(Option.isNone(result))
}).pipe(Effect.provide(TestClock.layer()))

const outcome = await Effect.runPromise(test)
outcome // => undefined
```

A key point is forking the fiber where `Effect.sleep` is invoked. Calls to `Effect.sleep` and related methods wait until the clock time matches or exceeds the scheduled time for their execution. By forking the fiber, we retain control over the clock time adjustments.

> **Best Practices**
>
> A recommended pattern when using the `TestClock` is to fork the effect being
> tested, adjust the clock time as needed, and then verify that the expected
> outcomes have occurred.

## Testing Recurring Effects

Here's an example demonstrating how to test an effect that runs at fixed intervals using the `TestClock`:

**Example** (Testing an Effect with Fixed Intervals)

In this example, we test an effect that runs at regular intervals. An unbounded queue is used to manage the effects, and we verify the following:

1. No effect occurs before the specified recurrence period.
2. An effect occurs after the recurrence period.
3. The effect executes exactly once.

```ts
import { Effect, Queue, Option } from "effect"
import { TestClock } from "effect/testing"
import * as assert from "node:assert"

const test = Effect.gen(function* () {
  const q = yield* Queue.unbounded()

  yield* Queue.offer(q, undefined).pipe(
    // Delay the effect for 60 minutes and repeat it forever
    Effect.delay("60 minutes"),
    Effect.forever,
    Effect.forkChild,
  )

  // Check if no effect is performed before the recurrence period
  const a = yield* Queue.poll(q).pipe(Effect.map(Option.isNone))

  // Adjust the TestClock by 60 minutes to simulate the passage of time
  yield* TestClock.adjust("60 minutes")

  // Check if an effect is performed after the recurrence period
  const b = yield* Queue.take(q).pipe(Effect.as(true))

  // Check if the effect is performed exactly once
  const c = yield* Queue.poll(q).pipe(Effect.map(Option.isNone))

  // Adjust the TestClock by another 60 minutes
  yield* TestClock.adjust("60 minutes")

  // Check if another effect is performed
  const d = yield* Queue.take(q).pipe(Effect.as(true))
  const e = yield* Queue.poll(q).pipe(Effect.map(Option.isNone))

  // Ensure that all conditions are met
  assert.ok(a && b && c && d && e)
}).pipe(Effect.provide(TestClock.layer()))

const outcome = await Effect.runPromise(test)
outcome // => undefined
```

It's important to note that after each recurrence, the next occurrence is scheduled to happen at the appropriate time. Adjusting the clock by 60 minutes places exactly one value in the queue; adjusting by another 60 minutes adds another value.

## Testing Clock

This example demonstrates how to test the behavior of the `Clock` using the `TestClock`:

**Example** (Simulating Time Passage with TestClock)

```ts
import { Effect, Clock } from "effect"
import { TestClock } from "effect/testing"
import * as assert from "node:assert"

const test = Effect.gen(function* () {
  // Get the current time using the Clock
  const startTime = yield* Clock.currentTimeMillis

  // Adjust the TestClock by 1 minute to simulate the passage of time
  yield* TestClock.adjust("1 minute")

  // Get the current time again
  const endTime = yield* Clock.currentTimeMillis

  // Check if the time difference is at least
  // 60,000 milliseconds (1 minute)
  assert.ok(endTime - startTime >= 60_000)
}).pipe(Effect.provide(TestClock.layer()))

const outcome = await Effect.runPromise(test)
outcome // => undefined
```

## Testing Deferred

The `TestClock` also impacts asynchronous code scheduled to run after a specific time.

**Example** (Simulating Delayed Execution with Deferred and TestClock)

```ts
import { Effect, Deferred } from "effect"
import { TestClock } from "effect/testing"
import * as assert from "node:assert"

const test = Effect.gen(function* () {
  // Create a deferred value
  const deferred = yield* Deferred.make<number, void>()

  // Run two effects concurrently: sleep for 10 seconds and succeed
  // the deferred with a value of 1
  yield* Effect.all(
    [Effect.sleep("10 seconds"), Deferred.succeed(deferred, 1)],
    {
      concurrency: "unbounded",
    },
  ).pipe(Effect.forkChild)

  // Adjust the TestClock by 10 seconds
  yield* TestClock.adjust("10 seconds")

  // Await the value from the deferred
  const readRef = yield* Deferred.await(deferred)

  // Verify the deferred value is correctly set
  assert.ok(readRef === 1)
}).pipe(Effect.provide(TestClock.layer()))

const outcome = await Effect.runPromise(test)
outcome // => undefined
```
