use std::path::PathBuf;
use kanade_shared::ipc::handshake::HandshakeSession;
use kanade_shared::ipc::state::StateSnapshot;
use kanade_shared::wire::EffectiveConfig;
use tokio::sync::{mpsc, watch};
use super::auth::PeerCredentials;
use super::subscriptions::SubscriptionRegistry;
pub struct ConnectionState {
pub peer: PeerCredentials,
pub pc_id: String,
pub agent_version: String,
pub config_rx: watch::Receiver<EffectiveConfig>,
pub state_rx: watch::Receiver<StateSnapshot>,
pub log_path: PathBuf,
pub subscriptions: SubscriptionRegistry,
pub push_tx: mpsc::Sender<Vec<u8>>,
agreed_protocol: Option<u32>,
}
impl ConnectionState {
#[allow(clippy::too_many_arguments)]
pub fn new(
peer: PeerCredentials,
pc_id: String,
agent_version: String,
config_rx: watch::Receiver<EffectiveConfig>,
state_rx: watch::Receiver<StateSnapshot>,
log_path: PathBuf,
push_tx: mpsc::Sender<Vec<u8>>,
) -> Self {
Self {
peer,
pc_id,
agent_version,
config_rx,
state_rx,
log_path,
subscriptions: SubscriptionRegistry::new(),
push_tx,
agreed_protocol: None,
}
}
pub fn handshake_complete(&self) -> bool {
self.agreed_protocol.is_some()
}
pub fn mark_handshake(&mut self, protocol: u32) {
self.agreed_protocol = Some(protocol);
}
pub fn session(&self) -> HandshakeSession {
HandshakeSession {
user: self.peer.user.clone(),
session_id: self.peer.session_id,
pc_id: self.pc_id.clone(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn dummy_peer() -> PeerCredentials {
PeerCredentials {
user: "DOMAIN\\alice".into(),
session_id: 2,
}
}
fn dummy_snapshot() -> StateSnapshot {
StateSnapshot {
pc_id: "PC1234".into(),
online: true,
vpn: "unknown".into(),
checks: vec![],
agent_version: "0.41.0".into(),
target_version: "0.41.0".into(),
}
}
fn fresh_state() -> ConnectionState {
let (_cfg_tx, cfg_rx) = watch::channel(EffectiveConfig::builtin_defaults());
let (_state_tx, state_rx) = watch::channel(dummy_snapshot());
let (push_tx, _push_rx) = mpsc::channel(8);
ConnectionState::new(
dummy_peer(),
"PC1234".into(),
"0.41.0".into(),
cfg_rx,
state_rx,
PathBuf::from("agent.log"),
push_tx,
)
}
#[tokio::test]
async fn fresh_connection_is_pre_handshake() {
let s = fresh_state();
assert!(!s.handshake_complete());
}
#[tokio::test]
async fn mark_handshake_unlocks_method_set() {
let mut s = fresh_state();
s.mark_handshake(1);
assert!(s.handshake_complete());
}
#[tokio::test]
async fn session_uses_authoritative_os_identity() {
let s = fresh_state();
let session = s.session();
assert_eq!(session.user, "DOMAIN\\alice");
assert_eq!(session.session_id, 2);
assert_eq!(session.pc_id, "PC1234");
}
}