1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//! Tests for `telegram_send::resolve_thread_id` — the helper that
//! lets the agent pass an explicit `thread_id` to override the
//! auto-lookup behavior added in commit `1e46cd26` (closes #130).
//!
//! Use cases the override enables:
//! * Cron jobs posting to a specific topic (e.g. daily summary in
//! #announcements regardless of what was discussed last in #dev).
//! * Multi-topic conversations where the agent wants to post into
//! a topic OTHER than the most recent one stored.
//! * Explicit user instruction: "post this in topic 17".
//!
//! Suggested by leshchenko1979 on issue #130:
//! https://github.com/adolfousier/opencrabs/issues/130#issuecomment-4582189795
use crate::brain::tools::telegram_send::resolve_thread_id;
use serde_json::json;
use teloxide::types::{MessageId, ThreadId};
#[tokio::test]
async fn explicit_thread_id_is_returned_verbatim() {
let input = json!({ "thread_id": 17 });
let result = resolve_thread_id(&input, 0).await;
assert_eq!(result, Some(ThreadId(MessageId(17))));
}
#[tokio::test]
async fn explicit_thread_id_works_for_negative_legacy_thread_ids() {
// Legacy Telegram chat shapes occasionally surface negative
// thread_id values within i32 range. The helper must pass them
// through, not reject them.
let input = json!({ "thread_id": -2147483 });
let result = resolve_thread_id(&input, 0).await;
assert_eq!(result, Some(ThreadId(MessageId(-2147483))));
}
#[tokio::test]
async fn explicit_thread_id_overflowing_i32_falls_back_to_lookup() {
// teloxide's ThreadId wraps MessageId(i32). Values past i32::MAX
// can't be represented. Rather than returning a wrong/zero ID,
// the helper falls through to the auto-lookup path.
let input = json!({ "thread_id": 9_999_999_999_i64 });
// No global pool initialised → auto-lookup returns None → final
// result is None. We just confirm no panic + no garbage value.
let result = resolve_thread_id(&input, 12345).await;
assert_eq!(result, None);
}
#[tokio::test]
async fn no_explicit_thread_id_falls_back_to_lookup() {
// Absent field → auto-lookup path. In tests the global pool
// isn't initialised so the lookup returns None; the important
// contract is "no override path, no garbage value, no panic".
let input = json!({ "chat_id": 12345 });
let result = resolve_thread_id(&input, 12345).await;
assert_eq!(result, None);
}
#[tokio::test]
async fn non_integer_thread_id_falls_back_to_lookup() {
// Defensive: the agent could emit "thread_id": "17" (string) or
// an object/array. Don't accept those as overrides — fall back
// to auto-lookup so a malformed override doesn't poison routing.
let input = json!({ "thread_id": "17" });
let result = resolve_thread_id(&input, 12345).await;
// Auto-lookup returns None in test (no global pool).
assert_eq!(result, None);
}
#[tokio::test]
async fn explicit_thread_id_zero_is_returned() {
// Telegram's General topic is sometimes represented as thread 0
// depending on API surface. The helper doesn't second-guess
// i32-valid values.
let input = json!({ "thread_id": 0 });
let result = resolve_thread_id(&input, 0).await;
assert_eq!(result, Some(ThreadId(MessageId(0))));
}