Repository navigation
Expand file tree
/
Copy pathbuiltin_plugin_directories_test.rs
More file actions
135 lines (119 loc) · 4.18 KB
/
Copy pathbuiltin_plugin_directories_test.rs
File metadata and controls
135 lines (119 loc) · 4.18 KB
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#![allow(clippy::unwrap_used)]
use std::path::PathBuf;
use github_copilot_sdk::{CliProgram, Client, ClientOptions, ErrorKind, Transport};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use tokio::net::TcpListener;
async fn read_framed(reader: &mut (impl AsyncRead + Unpin)) -> serde_json::Value {
let mut header = String::new();
loop {
let mut byte = [0u8; 1];
reader.read_exact(&mut byte).await.unwrap();
header.push(byte[0] as char);
if header.ends_with("\r\n\r\n") {
break;
}
}
let length = header
.trim()
.strip_prefix("Content-Length: ")
.unwrap()
.parse()
.unwrap();
let mut body = vec![0; length];
reader.read_exact(&mut body).await.unwrap();
serde_json::from_slice(&body).unwrap()
}
async fn write_result(
writer: &mut (impl AsyncWrite + Unpin),
request: &serde_json::Value,
result: serde_json::Value,
) {
let body = serde_json::to_vec(&serde_json::json!({
"jsonrpc": "2.0",
"id": request["id"],
"result": result,
}))
.unwrap();
writer
.write_all(format!("Content-Length: {}\r\n\r\n", body.len()).as_bytes())
.await
.unwrap();
writer.write_all(&body).await.unwrap();
writer.flush().await.unwrap();
}
async fn run_start(paths: Option<Vec<PathBuf>>) -> Vec<serde_json::Value> {
let listener = TcpListener::bind(("127.0.0.1", 0)).await.unwrap();
let address = listener.local_addr().unwrap();
let expect_builtin = paths.as_ref().is_some_and(|paths| !paths.is_empty());
let server = tokio::spawn(async move {
let (stream, _) = listener.accept().await.unwrap();
let (mut reader, mut writer) = tokio::io::split(stream);
let mut requests = Vec::new();
let connect = read_framed(&mut reader).await;
write_result(
&mut writer,
&connect,
serde_json::json!({ "ok": true, "protocolVersion": 3, "version": "test" }),
)
.await;
requests.push(connect);
if expect_builtin {
let builtin = read_framed(&mut reader).await;
write_result(&mut writer, &builtin, serde_json::json!({})).await;
requests.push(builtin);
}
requests
});
let mut options = ClientOptions::new()
.with_program(CliProgram::Path(std::env::current_exe().unwrap()))
.with_transport(Transport::External {
host: address.ip().to_string(),
port: address.port(),
connection_token: None,
});
if let Some(paths) = paths {
options = options.with_builtin_plugin_directories(paths);
}
let client = Client::start(options).await.unwrap();
let requests = server.await.unwrap();
client.force_stop();
requests
}
#[tokio::test]
async fn default_and_empty_do_not_call_rpc() {
for paths in [None, Some(Vec::new())] {
let requests = run_start(paths).await;
assert_eq!(requests.len(), 1);
assert_eq!(requests[0]["method"], "connect");
}
}
#[tokio::test]
async fn configured_directories_call_rpc_once_before_start_completes() {
let cwd = std::env::current_dir().unwrap();
let paths = vec![cwd.join("plugins/core"), cwd.join("plugins/github")];
let requests = run_start(Some(paths.clone())).await;
assert_eq!(requests.len(), 2);
assert_eq!(requests[0]["method"], "connect");
assert_eq!(requests[1]["method"], "plugins.builtin.set");
assert_eq!(
requests[1]["params"],
serde_json::json!({
"paths": paths
.iter()
.map(|path| path.to_str().unwrap())
.collect::<Vec<_>>()
})
);
}
#[tokio::test]
async fn relative_directory_is_rejected() {
let options = ClientOptions::new()
.with_program(CliProgram::Path(std::env::current_exe().unwrap()))
.with_builtin_plugin_directories(["plugins/core"]);
let error = match Client::start(options).await {
Ok(_) => panic!("relative path unexpectedly accepted"),
Err(error) => error,
};
assert_eq!(error.kind(), &ErrorKind::InvalidConfig);
assert!(error.to_string().contains("absolute paths"));
}