- 1
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] - 2
- 3
use std::path::Path; - 4
use std::sync::Arc; - 5
- 6
use vak_core::Core; - 7
use vak_llm::stream; - 8
use vak_llm::types::ChatRequest; - 9
use vak_llm::{EventStream, LlmError, Provider}; - 10
- 11
struct Empty; - 12
- 13
#[async_trait::async_trait] - 14
impl Provider for Empty { - 15
fn name(&self) -> &str { - 16
"empty" - 17
} - 18
- 19
async fn stream( - 20
&self, - 21
_request: ChatRequest, - 22
_cancel: tokio_util::sync::CancellationToken, - 23
) -> Result<EventStream, LlmError> { - 24
let (mut sink, rx) = stream::channel(8); - 25
sink.close_error(LlmError::Parse("unused".into())).await; - 26
Ok(rx) - 27
} - 28
} - 29
- 30
fn header_for(cwd: &Path) -> String { - 31
let _ = cwd; - 32
String::new() - 33
} - 34
- 35
#[tokio::test(flavor = "multi_thread", worker_threads = 4)] - 36
async fn memory_and_proposal_endpoints() { - 37
let _ = header_for; - 38
let dir = tempfile::tempdir().unwrap(); - 39
let home = dir.path().join("home"); - 40
let cwd = dir.path().to_path_buf(); - 41
- 42
vak_config::paths::isolate_home_for_tests(); - 43
let core = Core::new(cwd.clone()).unwrap(); - 44
core.set_sessions_home(home.clone()); - 45
core.set_provider_instance(Arc::new(Empty)); - 46
std::mem::forget(dir); - 47
- 48
// Seed one durable note and one pending proposal through the same APIs - 49
// the tools use. - 50
vak_core::memory::append_note( - 51
&home, - 52
&cwd, - 53
"preference", - 54
"style", - 55
"seed-session", - 56
"prefer small PRs", - 57
) - 58
.unwrap(); - 59
let pdir = home - 60
.join("skill-proposals") - 61
.join(vak_core::memory::hash_cwd(&cwd)); - 62
std::fs::create_dir_all(&pdir).unwrap(); - 63
std::fs::write( - 64
pdir.join("aabbccddeeff11223344556677889900112233445566778899aabbccddeeff00.md"), - 65
"---\nname: \"test-skill\"\ndescription: \"A queued skill\"\n---\n\nDo the thing.\n", - 66
) - 67
.unwrap(); - 68
- 69
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); - 70
let addr = listener.local_addr().unwrap(); - 71
tokio::spawn(async move { - 72
axum::serve(listener, vak_server::router(core)) - 73
.await - 74
.unwrap(); - 75
}); - 76
let base = format!("http://{addr}"); - 77
let client = reqwest::Client::new(); - 78
- 79
// Memory listing. - 80
let res = client.get(format!("{base}/memory")).send().await.unwrap(); - 81
assert_eq!(res.status(), 200); - 82
let body: serde_json::Value = res.json().await.unwrap(); - 83
let notes = body["notes"].as_array().unwrap(); - 84
assert_eq!(notes.len(), 1); - 85
assert_eq!(notes[0]["kind"], "preference"); - 86
assert_eq!(notes[0]["tag"], "style"); - 87
- 88
// Proposal queue → promote → gone from queue, present in discovery. - 89
let res = client - 90
.get(format!("{base}/skills/proposals")) - 91
.send() - 92
.await - 93
.unwrap(); - 94
assert_eq!(res.status(), 200); - 95
let body: serde_json::Value = res.json().await.unwrap(); - 96
let proposals = body["proposals"].as_array().unwrap(); - 97
assert_eq!(proposals.len(), 1); - 98
assert_eq!(proposals[0]["name"], "test-skill"); - 99
let id = proposals[0]["id"].as_str().unwrap().to_string(); - 100
- 101
let res = client - 102
.post(format!("{base}/skills/proposals/{id}/promote")) - 103
.send() - 104
.await - 105
.unwrap(); - 106
assert_eq!(res.status(), 200); - 107
let body: serde_json::Value = res.json().await.unwrap(); - 108
assert_eq!(body["promoted"], "test-skill"); - 109
- 110
let res = client - 111
.get(format!("{base}/skills/proposals")) - 112
.send() - 113
.await - 114
.unwrap(); - 115
let body: serde_json::Value = res.json().await.unwrap(); - 116
assert_eq!(body["proposals"].as_array().unwrap().len(), 0); - 117
assert!(home.join("skills/test-skill/SKILL.md").exists()); - 118
- 119
// Unknown id answers 404-shaped error, not a panic. - 120
let res = client - 121
.post(format!("{base}/skills/proposals/nope/reject")) - 122
.send() - 123
.await - 124
.unwrap(); - 125
assert_eq!(res.status(), 404); - 126
} - 127
Indexing the workspace…
Vakyartha documentation is discovering safe artifacts, anchors, and source references.