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) })