105 lines
3.5 KiB
JavaScript
105 lines
3.5 KiB
JavaScript
import test from "node:test"
|
|
import assert from "node:assert/strict"
|
|
import fs from "node:fs"
|
|
import os from "node:os"
|
|
import path from "node:path"
|
|
|
|
import { buildReplyEndState, clearReplyEndStateForConversation, getReplyEndState, setReplyEndState } from "../src/core/state.ts"
|
|
import { evaluateReplyEndPolicy } from "../src/core/policy.ts"
|
|
import { parseTelegramCallbackData, normalizeTelegramCallback } from "../src/core/callback-contract.ts"
|
|
import { resolveReplyEndStateFile, readReplyEndStateMap, saveReplyEndState, loadReplyEndState, removeReplyEndState } from "../src/core/store.ts"
|
|
|
|
test("parseTelegramCallbackData parses continue and stop", () => {
|
|
assert.equal(parseTelegramCallbackData("rec:continue"), "continue")
|
|
assert.equal(parseTelegramCallbackData("rec:stop"), "stop")
|
|
assert.equal(parseTelegramCallbackData("other"), null)
|
|
})
|
|
|
|
test("normalizeTelegramCallback returns normalized event", () => {
|
|
const event = normalizeTelegramCallback({
|
|
callbackData: "rec:continue",
|
|
conversationId: "telegram:1",
|
|
sessionKey: "session-1",
|
|
sourceMessageId: "100",
|
|
sourceCallbackId: "cb-1",
|
|
timestamp: "2026-05-13T00:00:00Z",
|
|
})
|
|
assert.deepEqual(event, {
|
|
choice: "continue",
|
|
conversationId: "telegram:1",
|
|
sessionKey: "session-1",
|
|
sourceMessageId: "100",
|
|
sourceCallbackId: "cb-1",
|
|
channel: "telegram",
|
|
timestamp: "2026-05-13T00:00:00Z",
|
|
})
|
|
})
|
|
|
|
test("state helpers build and update reply-end state", () => {
|
|
const state = buildReplyEndState({
|
|
choice: "stop",
|
|
conversationId: "telegram:1",
|
|
sessionKey: null,
|
|
sourceMessageId: "101",
|
|
sourceCallbackId: "cb-2",
|
|
channel: "telegram",
|
|
timestamp: "2026-05-13T00:01:00Z",
|
|
})
|
|
let stateMap = {}
|
|
stateMap = setReplyEndState(stateMap, "telegram:1", state)
|
|
assert.equal(getReplyEndState(stateMap, "telegram:1")?.lastChoice, "stop")
|
|
stateMap = clearReplyEndStateForConversation(stateMap, "telegram:1")
|
|
assert.equal(getReplyEndState(stateMap, "telegram:1"), null)
|
|
})
|
|
|
|
test("policy suppresses continuation only for active stop without typed follow-up", () => {
|
|
assert.deepEqual(evaluateReplyEndPolicy({ state: null, hasTypedUserFollowup: false }), {
|
|
suppressProactiveContinuation: false,
|
|
})
|
|
assert.deepEqual(
|
|
evaluateReplyEndPolicy({
|
|
state: {
|
|
lastChoice: "stop",
|
|
lastChoiceAt: "2026-05-13T00:02:00Z",
|
|
sourceMessageId: "102",
|
|
sourceCallbackId: "cb-3",
|
|
active: true,
|
|
},
|
|
hasTypedUserFollowup: false,
|
|
}),
|
|
{ suppressProactiveContinuation: true }
|
|
)
|
|
assert.deepEqual(
|
|
evaluateReplyEndPolicy({
|
|
state: {
|
|
lastChoice: "stop",
|
|
lastChoiceAt: "2026-05-13T00:02:00Z",
|
|
sourceMessageId: "102",
|
|
sourceCallbackId: "cb-3",
|
|
active: true,
|
|
},
|
|
hasTypedUserFollowup: true,
|
|
}),
|
|
{ suppressProactiveContinuation: false }
|
|
)
|
|
})
|
|
|
|
test("store helpers persist and load reply-end state", () => {
|
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "reply-end-controls-"))
|
|
const filePath = resolveReplyEndStateFile(tempDir)
|
|
const state = {
|
|
lastChoice: "continue",
|
|
lastChoiceAt: "2026-05-13T00:03:00Z",
|
|
sourceMessageId: "103",
|
|
sourceCallbackId: "cb-4",
|
|
active: true,
|
|
}
|
|
saveReplyEndState(filePath, "telegram:2", state)
|
|
const map = readReplyEndStateMap(filePath)
|
|
assert.equal(map["telegram:2"].lastChoice, "continue")
|
|
const loaded = loadReplyEndState(filePath, "telegram:2")
|
|
assert.equal(loaded?.sourceMessageId, "103")
|
|
removeReplyEndState(filePath, "telegram:2")
|
|
assert.equal(loadReplyEndState(filePath, "telegram:2"), null)
|
|
})
|