Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,22 @@ export const make = <
// Raw RivetKit clients call no-argument actions with an
// absent first argument. The Effect JSON Void codec expects
// null, so adapt only actions that declared no payload.
// Additionally, scheduled no-payload actions receive ScheduledFireInfo
// in the payload position, which must also decode as null.
const isScheduledFireInfo = (val: unknown): boolean =>
typeof val === "object" &&
val !== null &&
"kind" in val &&
"scheduledAt" in val &&
"firedAt" in val;

const isMisalignedSchedulePayload =
!action.hasPayload &&
meta === undefined &&
isScheduledFireInfo(payload);

const payloadForDecode =
!action.hasPayload && payload === undefined
!action.hasPayload && (payload === undefined || isMisalignedSchedulePayload)
? null
: payload;
const decodedPayload = yield* decodePayload(
Expand Down
22 changes: 22 additions & 0 deletions rivetkit-typescript/packages/effect/test/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {
Unregistered,
WakeDecodeFail,
WakeDecodeFailLive,
ScheduleReproActor,
ScheduleReproActorLive,
} from "./fixtures/actors";
import { TestTracer } from "./fixtures/tracer";
import { prepareNamespace, waitForEnvoy } from "./shared-engine";
Expand Down Expand Up @@ -81,6 +83,7 @@ const TestLayer = ReadyForEnvoy.pipe(
WakeDecodeFailLive,
BuildSetRejectedLive,
TransformedStateActorLive,
ScheduleReproActorLive,
),
),
Layer.provideMerge(Flags.layer),
Expand All @@ -100,6 +103,25 @@ const TestLayer = ReadyForEnvoy.pipe(
);

layer(TestLayer)("end-to-end", (it) => {
it.effect("scheduled no-payload actions execute successfully", () =>
Effect.gen(function* () {
const flags = yield* Flags;
const client = yield* ScheduleReproActor.client;
const actor = client.getOrCreate("t-schedule");

yield* actor.ScheduleReproSchedule();

const fired = yield* Effect.sync(() => flags.get("schedule_repro_fired")).pipe(
Effect.repeat({
until: (v) => v === true,
schedule: Schedule.spaced("100 millis"),
}),
TestClock.withLive,
);
assert.strictEqual(fired, true);
}),
);

it.effect("round-trips an action with payload and success", () =>
Effect.gen(function* () {
const counter = (yield* Counter.client).getOrCreate("t-roundtrip");
Expand Down
26 changes: 26 additions & 0 deletions rivetkit-typescript/packages/effect/test/fixtures/actors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -733,3 +733,29 @@ export const BuildSetRejectedLive = BuildSetRejected.toLayer(
},
},
);

export const ScheduleReproAction = Action.make("ScheduleReproAction");
export const ScheduleReproSchedule = Action.make("ScheduleReproSchedule");
export const ScheduleReproActor = Actor.make("ScheduleReproActor", {
actions: [ScheduleReproAction, ScheduleReproSchedule],
});
export const ScheduleReproActorLive = ScheduleReproActor.toLayer(
({ rawRivetkitContext }) =>
Effect.gen(function* () {
const flags = yield* Flags;
return ScheduleReproActor.of({
ScheduleReproAction: () =>
Effect.sync(() => {
flags.set("schedule_repro_fired", true);
return null;
}),
ScheduleReproSchedule: () =>
Effect.gen(function* () {
yield* Effect.promise(() =>
rawRivetkitContext.schedule.after(100, "ScheduleReproAction"),
);
return null;
}),
});
}),
);