blob: d2fed58e8e1b07e24ceef8343f4a0d687c9b86a7 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
use crate::support::context as support;
use maka_agent_engine::RunError;
use maka_agent_facts::event::Fact;
use maka_agent_facts::event::InvocationOutcome;
use maka_agent_facts::event::ModelInterruption;
use maka_event_log::EventLog;
use std::sync::Arc;
use support::*;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::TcpListener,
};
use tokio_util::sync::CancellationToken;
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn stopping_summary_drains_attempt_without_checkpoint_or_retry() {
let directory = tempfile::tempdir().unwrap();
let log = Arc::new(
EventLog::open(&directory.path().join("events.sqlite"))
.await
.unwrap(),
);
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let base = format!("http://{}/v1", listener.local_addr().unwrap());
let (seen, ready) = tokio::sync::oneshot::channel();
let server = tokio::spawn(async move {
let (mut old, _) = listener.accept().await.unwrap();
read_request(&mut old).await;
respond(&mut old, SUMMARY, "stop").await;
let (mut summary, _) = listener.accept().await.unwrap();
read_request(&mut summary).await;
summary
.write_all(
b"HTTP/1.1 200 OK\r\nContent-Type: text/event-stream\r\nConnection: close\r\n\r\n",
)
.await
.unwrap();
seen.send(()).unwrap();
let mut bytes = Vec::new();
let _ = summary.read_to_end(&mut bytes).await;
});
let worker = engine(log.clone());
worker
.run(input(&base, "old", false), CancellationToken::new())
.await
.unwrap();
let running = worker
.start(input(&base, "stop", true), CancellationToken::new())
.await
.unwrap();
ready.await.unwrap();
running.cancel();
assert!(matches!(
running.wait().await,
Err(RunError::Cancelled) | Err(RunError::Model(maka_model::ModelError::Cancelled))
));
worker.drain().await;
server.await.unwrap();
let prefix = log.prefix(100, 128 * 1024).await.unwrap();
let stopped: Vec<_> = prefix
.events
.iter()
.filter(|event| event.event.invocation.invocation_id == "invocation-stop")
.collect();
assert_eq!(
stopped
.iter()
.filter(|event| matches!(event.event.fact, Fact::ModelRequested { .. }))
.count(),
1
);
assert!(stopped.iter().any(|event| matches!(
event.event.fact,
Fact::ModelInterrupted {
status: ModelInterruption::Cancelled,
model_failure: None,
..
}
)));
assert!(stopped.iter().any(|event| matches!(
event.event.fact,
Fact::InvocationEnded {
outcome: InvocationOutcome::Cancelled { .. }
}
)));
assert!(
!prefix
.events
.iter()
.any(|event| matches!(event.event.fact, Fact::ContextCheckpointRecorded { .. }))
);
assert!(
log.read_model_context("session", None)
.await
.unwrap()
.baseline
.is_none()
);
}