import { EventId, ThreadId } from "@t3tools/contracts";import { assert, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as SqlClient from "effect/unstable/sql/SqlClient"; import { ProjectionThreadActivityRepository } from "../Services/ProjectionThreadActivities.ts";import { ProjectionThreadActivityRepositoryLive } from "./ProjectionThreadActivities.ts";import { SqlitePersistenceMemory } from "./Sqlite.ts"; const layer = it.layer( ProjectionThreadActivityRepositoryLive.pipe(Layer.provideMerge(SqlitePersistenceMemory)),); layer("ProjectionThreadActivityRepository", (it) => { it.effect("reads only the latest matching task activity", () => Effect.gen(function* () { const repository = yield* ProjectionThreadActivityRepository; const sql = yield* SqlClient.SqlClient; const threadId = ThreadId.make("thread-latest-task-activity"); yield* sql` INSERT INTO projection_thread_activities ( activity_id, thread_id, turn_id, tone, kind, summary, payload_json, sequence, created_at ) VALUES ( 'activity-task-unrelated-tool', ${threadId}, NULL, 'tool', 'tool.completed', 'large tool output', 'not-json', 1, '2026-03-01T00:00:00.000Z' ), ( 'activity-task-started', ${threadId}, NULL, 'info', 'task.started', 'started', '{"taskId":"task-1","title":"Initial title"}', 2, '2026-03-01T00:00:01.000Z' ), ( 'activity-task-progress', ${threadId}, NULL, 'info', 'task.progress', 'progress', '{"taskId":"task-1","title":"Updated title"}', 3, '2026-03-01T00:00:02.000Z' ), ( 'activity-task-other', ${threadId}, NULL, 'info', 'task.progress', 'other', '{"taskId":"task-2","title":"Other title"}', 4, '2026-03-01T00:00:03.000Z' ) `; yield* repository.upsert({ activityId: EventId.make("activity-task-untitled"), threadId, turnId: null, tone: "info", kind: "task.progress", summary: "Still running", payload: { taskId: "task-1" }, sequence: 5, createdAt: "2026-03-01T00:00:04.000Z", }); yield* repository.upsert({ activityId: EventId.make("activity-task-blank-title"), threadId, turnId: null, tone: "info", kind: "task.progress", summary: "Still running", payload: { taskId: "task-1", title: " \t\n\u00a0" }, sequence: 6, createdAt: "2026-03-01T00:00:05.000Z", }); const recent = yield* repository.listByThreadId({ threadId, activityKinds: ["task.progress"], limit: 2, }); assert.deepEqual( recent.map((entry) => entry.activityId), ["activity-task-untitled", "activity-task-blank-title"], ); const activity = yield* repository.getLatestTaskActivity({ threadId, taskId: "task-1", }); assert.equal(activity._tag, "Some"); if (activity._tag === "Some") { assert.equal(activity.value.activityId, EventId.make("activity-task-progress")); assert.deepEqual(activity.value.payload, { taskId: "task-1", title: "Updated title", }); } assert.equal( (yield* repository.getLatestTaskActivity({ threadId, taskId: "missing" }))._tag, "None", ); }), );});
import { EventId, ThreadId } from "@t3tools/contracts";
import { assert, it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as SqlClient from "effect/unstable/sql/SqlClient";
import { ProjectionThreadActivityRepository } from "../Services/ProjectionThreadActivities.ts";
import { ProjectionThreadActivityRepositoryLive } from "./ProjectionThreadActivities.ts";
import { SqlitePersistenceMemory } from "./Sqlite.ts";
const layer = it.layer(
ProjectionThreadActivityRepositoryLive.pipe(Layer.provideMerge(SqlitePersistenceMemory)),
);
layer("ProjectionThreadActivityRepository", (it) => {
it.effect("reads only the latest matching task activity", () =>
Effect.gen(function* () {
const repository = yield* ProjectionThreadActivityRepository;
const sql = yield* SqlClient.SqlClient;
const threadId = ThreadId.make("thread-latest-task-activity");
yield* sql`
INSERT INTO projection_thread_activities (
activity_id, thread_id, turn_id, tone, kind, summary, payload_json, sequence, created_at
)
VALUES
(
'activity-task-unrelated-tool', ${threadId}, NULL, 'tool', 'tool.completed',
'large tool output', 'not-json', 1, '2026-03-01T00:00:00.000Z'
),
(
'activity-task-started', ${threadId}, NULL, 'info', 'task.started',
'started', '{"taskId":"task-1","title":"Initial title"}', 2,
'2026-03-01T00:00:01.000Z'
),
(
'activity-task-progress', ${threadId}, NULL, 'info', 'task.progress',
'progress', '{"taskId":"task-1","title":"Updated title"}', 3,
'2026-03-01T00:00:02.000Z'
),
(
'activity-task-other', ${threadId}, NULL, 'info', 'task.progress',
'other', '{"taskId":"task-2","title":"Other title"}', 4,
'2026-03-01T00:00:03.000Z'
)
`;
yield* repository.upsert({
activityId: EventId.make("activity-task-untitled"),
threadId,
turnId: null,
tone: "info",
kind: "task.progress",
summary: "Still running",
payload: { taskId: "task-1" },
sequence: 5,
createdAt: "2026-03-01T00:00:04.000Z",
});
yield* repository.upsert({
activityId: EventId.make("activity-task-blank-title"),
threadId,
turnId: null,
tone: "info",
kind: "task.progress",
summary: "Still running",
payload: { taskId: "task-1", title: " \t\n\u00a0" },
sequence: 6,
createdAt: "2026-03-01T00:00:05.000Z",
});
const recent = yield* repository.listByThreadId({
threadId,
activityKinds: ["task.progress"],
limit: 2,
});
assert.deepEqual(
recent.map((entry) => entry.activityId),
["activity-task-untitled", "activity-task-blank-title"],
);
const activity = yield* repository.getLatestTaskActivity({
threadId,
taskId: "task-1",
});
assert.equal(activity._tag, "Some");
if (activity._tag === "Some") {
assert.equal(activity.value.activityId, EventId.make("activity-task-progress"));
assert.deepEqual(activity.value.payload, {
taskId: "task-1",
title: "Updated title",
});
}
assert.equal(
(yield* repository.getLatestTaskActivity({ threadId, taskId: "missing" }))._tag,
"None",
);
}),
);
});