Skip to content

Commit facb80b

Browse files
committed
test shared context
1 parent 511bccb commit facb80b

2 files changed

Lines changed: 75 additions & 4 deletions

File tree

packages/infra/test/fixtures.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Effect, Layer, S, Scope, Context } from "effect-app"
1+
import { Context, Effect, Layer, S, Scope } from "effect-app"
22
import { NotLoggedInError, UnauthorizedError } from "effect-app/client"
33
import { RpcContextMap, RpcX } from "effect-app/rpc"
44
import { TaggedErrorClass } from "effect-app/Schema"
@@ -128,5 +128,7 @@ export const TestLive = Layer.effect(
128128
})
129129
)
130130

131+
export class Counter extends Context.Opaque<Counter>()("Counter", { make: Effect.succeed({ a: 0 }) }) {}
132+
131133
export class CustomError1 extends TaggedErrorClass<CustomError1>()("CustomError1", {}) {}
132134
export class CustomError2 extends TaggedErrorClass<CustomError2>()("CustomError2", {}) {}

packages/infra/test/rpc-multi-middleware.test.ts

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { NodeHttpServer } from "@effect/platform-node"
22
import { expect, expectTypeOf, it } from "@effect/vitest"
3-
import { Console, Effect, Layer, Result } from "effect"
4-
import { S } from "effect-app"
3+
import { Console, Effect, Layer, Ref, Result } from "effect"
4+
import { Context, S } from "effect-app"
55
import { NotLoggedInError } from "effect-app/client"
66
import { HttpRouter } from "effect-app/http"
77
import { DefaultGenericMiddlewares } from "effect-app/middleware"
88
import { MiddlewareMaker } from "effect-app/rpc"
99
import { middlewareGroup } from "effect-app/rpc/MiddlewareMaker"
1010
import { FetchHttpClient } from "effect/unstable/http"
11-
import { RpcClient, RpcGroup, RpcSerialization, RpcServer, RpcTest } from "effect/unstable/rpc"
11+
import { Rpc, RpcClient, RpcGroup, RpcSerialization, RpcServer, RpcTest } from "effect/unstable/rpc"
1212
import { createServer } from "http"
1313
import { DefaultGenericMiddlewaresLive } from "../src/api/routing.js"
1414
import { AllowAnonymous, AllowAnonymousLive, RequestContextMap, RequireRoles, RequireRolesLive, Some, SomeElseMiddleware, SomeElseMiddlewareLive, SomeMiddleware, SomeMiddlewareLive, SomeService, Test, TestLive, UserProfile } from "./fixtures.js"
@@ -136,3 +136,72 @@ it.live(
136136
Effect.provide(RpcTestLayer)
137137
)
138138
)
139+
140+
// Per-request service isolation test
141+
142+
class PerRequestCounter extends Context.Service<PerRequestCounter>()(
143+
"PerRequestCounter",
144+
{ make: Effect.sync(() => ({ a: 0 })) }
145+
) {
146+
static Default = Layer.effect(this, this.make)
147+
}
148+
149+
class GlobalCounter extends Context.Service<GlobalCounter, {
150+
readonly ref: Ref.Ref<number>
151+
}>()("GlobalCounter") {}
152+
153+
const CounterRpcs = RpcGroup.make(
154+
Rpc.make("incrementA", {
155+
success: S.Number
156+
}),
157+
Rpc.make("incrementB", {
158+
success: S.Number
159+
})
160+
)
161+
162+
const counterImpl = CounterRpcs
163+
.toLayer({
164+
incrementA: Effect.fn(function*() {
165+
const counter = yield* PerRequestCounter
166+
counter.a++
167+
const global = yield* GlobalCounter
168+
yield* Ref.update(global.ref, (n) => n + 1)
169+
return counter.a
170+
}, Effect.provide(PerRequestCounter.Default)),
171+
incrementB: Effect.fn(function*() {
172+
const counter = yield* PerRequestCounter
173+
counter.a++
174+
const global = yield* GlobalCounter
175+
yield* Ref.update(global.ref, (n) => n + 1)
176+
return counter.a
177+
}, Effect.provide(PerRequestCounter.Default))
178+
})
179+
180+
const GlobalCounterLive = Layer.effect(
181+
GlobalCounter,
182+
Ref.make(0).pipe(Effect.map((ref) => ({ ref })))
183+
)
184+
185+
const CounterTestLayer = counterImpl.pipe(Layer.provideMerge(GlobalCounterLive))
186+
187+
it.live(
188+
"per-request service isolation with shared global counter",
189+
Effect.fnUntraced(
190+
function*() {
191+
const client = yield* RpcTest.makeClient(CounterRpcs)
192+
const global = yield* GlobalCounter
193+
194+
const r1 = yield* client.incrementA()
195+
const r2 = yield* client.incrementB()
196+
197+
// per-request counter is fresh each time → both return 1
198+
expect(r1).toBe(1)
199+
expect(r2).toBe(1)
200+
201+
// global counter is shared across requests → accumulates to 2
202+
const globalCount = yield* Ref.get(global.ref)
203+
expect(globalCount).toBe(2)
204+
},
205+
Effect.provide(CounterTestLayer)
206+
)
207+
)

0 commit comments

Comments
 (0)