@@ -1,39 +1,36 @@
|
|||||||
import { Effect, Option, type Scope, Stream } from "effect"
|
import { Effect, type Scope, Stream } from "effect"
|
||||||
|
import { AsyncResult } from "effect/unstable/reactivity"
|
||||||
import { describe, expect, it } from "vitest"
|
import { describe, expect, it } from "vitest"
|
||||||
import * as Query from "../src/Query.js"
|
import * as Query from "../src/Query.js"
|
||||||
import * as QueryClient from "../src/QueryClient.js"
|
import * as QueryClient from "../src/QueryClient.js"
|
||||||
import * as Result from "../src/Result.js"
|
import * as View from "../src/View.js"
|
||||||
|
|
||||||
|
|
||||||
const runQueryTest = <A, E>(effect: Effect.Effect<A, E, QueryClient.QueryClient | Scope.Scope>) =>
|
const runQueryTest = <A, E>(effect: Effect.Effect<A, E, QueryClient.QueryClient | Scope.Scope>) =>
|
||||||
Effect.runPromise(Effect.scoped(effect.pipe(
|
Effect.runPromise(Effect.scoped(effect.pipe(
|
||||||
Effect.provide(QueryClient.QueryClient.Default),
|
Effect.provide(QueryClient.layer()),
|
||||||
)))
|
)))
|
||||||
|
|
||||||
const expectSuccessValue = <A, E, P>(
|
const staticKey = <K>(key: K): View.View<K> => View.make({
|
||||||
result: Result.Result<A, E, P>,
|
get: Effect.succeed(key),
|
||||||
|
changes: Stream.make(key),
|
||||||
|
})
|
||||||
|
|
||||||
|
const expectSuccessValue = <A, E>(
|
||||||
|
state: Query.FinalQueryState<unknown, A, E>,
|
||||||
): A => {
|
): A => {
|
||||||
expect(Result.isSuccess(result)).toBe(true)
|
expect(AsyncResult.isSuccess(state.result)).toBe(true)
|
||||||
|
|
||||||
if (!Result.isSuccess(result))
|
if (!AsyncResult.isSuccess(state.result))
|
||||||
throw new Error(`Expected Success result, received ${result._tag}`)
|
throw new Error(`Expected Success result, received ${state.result._tag}`)
|
||||||
|
|
||||||
return result.value
|
return state.result.value
|
||||||
}
|
|
||||||
|
|
||||||
const expectSomeValue = <A>(option: Option.Option<A>): A => {
|
|
||||||
expect(Option.isSome(option)).toBe(true)
|
|
||||||
|
|
||||||
if (!Option.isSome(option))
|
|
||||||
throw new Error("Expected Some option, received None")
|
|
||||||
|
|
||||||
return option.value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("Query", () => {
|
describe("Query", () => {
|
||||||
it("fetch caches successful results until they are invalidated or stale", async () => {
|
it("fetch caches successful results until they are invalidated or stale", async () => {
|
||||||
let calls = 0
|
let calls = 0
|
||||||
const key = Stream.empty as Stream.Stream<readonly [number]>
|
const key = staticKey<readonly [number]>([1])
|
||||||
|
|
||||||
const result = await runQueryTest(Effect.gen(function*() {
|
const result = await runQueryTest(Effect.gen(function*() {
|
||||||
const query = yield* Query.make({
|
const query = yield* Query.make({
|
||||||
@@ -52,15 +49,13 @@ describe("Query", () => {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
expect(calls).toBe(1)
|
expect(calls).toBe(1)
|
||||||
expect(result[0]._tag).toBe("Success")
|
|
||||||
expect(result[1]._tag).toBe("Success")
|
|
||||||
expect(expectSuccessValue(result[0])).toBe("value:1:1")
|
expect(expectSuccessValue(result[0])).toBe("value:1:1")
|
||||||
expect(expectSuccessValue(result[1])).toBe("value:1:1")
|
expect(expectSuccessValue(result[1])).toBe("value:1:1")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("refresh reruns the latest query key", async () => {
|
it("refresh reruns the latest query key", async () => {
|
||||||
let calls = 0
|
let calls = 0
|
||||||
const key = Stream.empty as Stream.Stream<readonly [number]>
|
const key = staticKey<readonly [number]>([1])
|
||||||
|
|
||||||
const result = await runQueryTest(Effect.gen(function*() {
|
const result = await runQueryTest(Effect.gen(function*() {
|
||||||
const query = yield* Query.make({
|
const query = yield* Query.make({
|
||||||
@@ -86,7 +81,7 @@ describe("Query", () => {
|
|||||||
|
|
||||||
it("invalidateCacheEntry forces the next fetch for that key to rerun", async () => {
|
it("invalidateCacheEntry forces the next fetch for that key to rerun", async () => {
|
||||||
let calls = 0
|
let calls = 0
|
||||||
const key = Stream.empty as Stream.Stream<readonly [number]>
|
const key = staticKey<readonly [number]>([1])
|
||||||
|
|
||||||
const result = await runQueryTest(Effect.gen(function*() {
|
const result = await runQueryTest(Effect.gen(function*() {
|
||||||
const query = yield* Query.make({
|
const query = yield* Query.make({
|
||||||
@@ -112,7 +107,7 @@ describe("Query", () => {
|
|||||||
|
|
||||||
it("invalidateCache clears cached entries for the query function", async () => {
|
it("invalidateCache clears cached entries for the query function", async () => {
|
||||||
let calls = 0
|
let calls = 0
|
||||||
const key = Stream.empty as Stream.Stream<readonly [number]>
|
const key = staticKey<readonly [number]>([1])
|
||||||
|
|
||||||
const result = await runQueryTest(Effect.gen(function*() {
|
const result = await runQueryTest(Effect.gen(function*() {
|
||||||
const query = yield* Query.make({
|
const query = yield* Query.make({
|
||||||
@@ -136,9 +131,9 @@ describe("Query", () => {
|
|||||||
expect(expectSuccessValue(result[1])).toBe("value:1:2")
|
expect(expectSuccessValue(result[1])).toBe("value:1:2")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("service starts the key stream automatically and updates latest state", async () => {
|
it("service starts the key view automatically and records its latest final state", async () => {
|
||||||
let calls = 0
|
let calls = 0
|
||||||
const key = Stream.make([1] as const) as Stream.Stream<readonly [number]>
|
const key = staticKey<readonly [number]>([1])
|
||||||
|
|
||||||
const effect = Effect.gen(function*() {
|
const effect = Effect.gen(function*() {
|
||||||
const query = yield* Query.service({
|
const query = yield* Query.service({
|
||||||
@@ -150,20 +145,23 @@ describe("Query", () => {
|
|||||||
staleTime: "1 minute",
|
staleTime: "1 minute",
|
||||||
})
|
})
|
||||||
|
|
||||||
yield* Effect.sleep("10 millis")
|
const latestFinalState = yield* Effect.sleep("1 millis").pipe(
|
||||||
|
Effect.andThen(View.get(query.latestFinalState)),
|
||||||
|
Effect.flatMap(Effect.fromOption),
|
||||||
|
Effect.eventually,
|
||||||
|
Effect.timeout("1 second"),
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
final: yield* query.result.get,
|
state: yield* View.get(query.state),
|
||||||
latestKey: yield* query.latestKey.get,
|
latestFinalState,
|
||||||
latestFinalResult: yield* query.latestFinalResult.get,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = await runQueryTest(effect)
|
const result = await runQueryTest(effect)
|
||||||
|
|
||||||
expect(calls).toBe(1)
|
expect(calls).toBe(1)
|
||||||
expect(expectSuccessValue(result.final)).toBe("value:1:1")
|
expect(result.state.key).toEqual([1])
|
||||||
expect(expectSomeValue(result.latestKey)).toEqual([1])
|
expect(expectSuccessValue(result.latestFinalState)).toBe("value:1:1")
|
||||||
expect(expectSuccessValue(expectSomeValue(result.latestFinalResult))).toBe("value:1:1")
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user