feat: publish Dever core with project scaffolding and versioned AI skills
This commit is contained in:
1 parent
70fa73cdc2
commit
ac458ffa7f
960 files changed
+221319
-1163
No files matched your search
@@ -0,0 +1,260 @@
|
||||
mod support;
|
||||
|
||||
use support::{checked, rejected, sources, stdout};
|
||||
|
||||
const CONTEXT: &str = r#"public type Context { name: Text
|
||||
client: dever.system.HttpClient
|
||||
events: Channel<Int> }
|
||||
route(request: dever.http.Request, context: Context) (response: dever.http.Response) {
|
||||
send(context.events, 1)
|
||||
response = dever.http.Response { status = 200
|
||||
headers = []
|
||||
body = dever.bytes.from_text(context.name) }
|
||||
}
|
||||
public serve(listener: dever.system.Listener, context: Context) () {
|
||||
dever.system.http_serve(route, listener, dever.http.default_limits(), context)
|
||||
}
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn typed_shared_context_and_async_effects_are_checked_before_emission() {
|
||||
let map = sources(CONTEXT);
|
||||
let api = checked(&map).api_snapshot();
|
||||
let serve = api
|
||||
.lines()
|
||||
.find(|line| line.starts_with("function main.serve"))
|
||||
.unwrap();
|
||||
assert!(
|
||||
serve.contains("network") && serve.contains("concurrency"),
|
||||
"{serve}"
|
||||
);
|
||||
rejected(
|
||||
&CONTEXT.replace("context: Context) (response", "context: Text) (response"),
|
||||
"signature",
|
||||
);
|
||||
rejected(
|
||||
&CONTEXT.replace("client: dever.system.HttpClient", "client: Stream<Int>"),
|
||||
"transferable",
|
||||
);
|
||||
rejected(
|
||||
&CONTEXT.replace(
|
||||
" send(context.events, 1)",
|
||||
" dever.io.println(context.name)",
|
||||
),
|
||||
"cannot run inline",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_construction_does_not_hide_unhandled_failures() {
|
||||
rejected(
|
||||
"type Result { Ok\nerror Failed(message: Text) }\nfail_work() () { fail(Result.Failed(\"bad\")) }\nwork() (value: Result) { value = result(fail_work()) }\nmain() () { values = stream([work()])\nclose(values) }",
|
||||
"failure",
|
||||
);
|
||||
rejected(
|
||||
"work() (value: dever.time.SleepResult) { value = result(dever.task.sleep(1)) }\nfallback() (value: dever.time.SleepResult) { value = dever.time.SleepResult.Done }\nmain() () { job = run(work())\nvalue = timeout(job, 10, fallback) }",
|
||||
"failure",
|
||||
);
|
||||
}
|
||||
|
||||
fn native_source(http2: bool) -> String {
|
||||
let root = include_str!("../fixtures/tls/root.pem");
|
||||
let cert = include_str!("../fixtures/tls/server.pem");
|
||||
let key = include_str!("../fixtures/tls/server-key.pem");
|
||||
let program = r#"type Context { prefix: Text
|
||||
client: dever.system.HttpClient
|
||||
hits: Channel<Int> }
|
||||
|
||||
request(target: Text) (value: dever.http.Request) {
|
||||
value = dever.http.Request { method = "GET"
|
||||
target = target
|
||||
headers = []
|
||||
body = dever.bytes.from_text("") }
|
||||
}
|
||||
serve(listener: dever.system.Listener, tls: dever.system.ServerTls, context: Context) () {
|
||||
limits = http_limits()
|
||||
# HTTP/1 的两条池连接之外,还需要一个独立 WSS 连接名额。
|
||||
limits.connections = 3
|
||||
dever.system.http_serve_live_tls(route, listener, limits, dever.http.default_live_limits(), tls, context)
|
||||
}
|
||||
copy_body(body: Bytes) (result: Bytes) { result = body }
|
||||
route(request: dever.http.Request, reply: dever.system.HttpReply, context: Context) () {
|
||||
child = run(copy_body(request.body))
|
||||
request.body = wait(child)
|
||||
send(context.hits, 1)
|
||||
path(request.target, request.body, reply, context)
|
||||
}
|
||||
path(target: "/proxy", body: Bytes, reply: dever.system.HttpReply, context: Context) () recover("test route reports failures") {
|
||||
proxy(result(dever.http.request(context.client, request("/value"))), reply)
|
||||
}
|
||||
path(target: "/events", body: Bytes, reply: dever.system.HttpReply, context: Context) () recover("test route reports failures") {
|
||||
dever.sse.start(reply, [])
|
||||
dever.sse.send(reply, dever.sse.Event { event = ""
|
||||
data = context.prefix
|
||||
id = null
|
||||
retry_ms = null })
|
||||
}
|
||||
path(target: "/upload", body: Bytes, reply: dever.system.HttpReply, context: Context) () recover("test route reports failures") {
|
||||
dever.http.respond(reply, dever.http.Response { status = 200
|
||||
headers = []
|
||||
body = body })
|
||||
}
|
||||
path(target: "/ws", body: Bytes, reply: dever.system.HttpReply, context: Context) () recover("test route reports failures") {
|
||||
upgraded(result(dever.websocket.accept(reply, dever.websocket.default_limits())))
|
||||
}
|
||||
path(target: other, body: Bytes, reply: dever.system.HttpReply, context: Context) () recover("test route reports failures") {
|
||||
dever.http.respond(reply, dever.http.Response { status = 200
|
||||
headers = []
|
||||
body = dever.bytes.from_text(context.prefix) })
|
||||
}
|
||||
proxy(result: dever.http.ResponseResult.Read(response), reply: dever.system.HttpReply) () recover("test proxy reports failure") {
|
||||
response.headers = []
|
||||
dever.http.respond(reply, response)
|
||||
}
|
||||
proxy(result: dever.http.ResponseResult.Failed(message), reply: dever.system.HttpReply) () recover("test proxy reports failure") {
|
||||
dever.http.respond(reply, dever.http.Response { status = 502
|
||||
headers = []
|
||||
body = dever.bytes.from_text(message) })
|
||||
}
|
||||
upgraded(result: dever.websocket.ConnectResult.Connected(socket)) () recover("test WebSocket policy") {
|
||||
each(echo, dever.websocket.messages(socket), socket)
|
||||
}
|
||||
upgraded(result: dever.websocket.ConnectResult.Failed(message)) () recover("test WebSocket policy") {}
|
||||
echo(event: dever.websocket.MessageEvent.Read(message), socket: dever.system.WebSocket) () recover("test WebSocket policy") {
|
||||
dever.websocket.send(socket, message)
|
||||
}
|
||||
echo(event: dever.websocket.MessageEvent.Failed(message), socket: dever.system.WebSocket) () recover("test WebSocket policy") {}
|
||||
received(result: dever.websocket.ReceiveResult.Done(state)) (ok: Bool) recover("test WebSocket receive") { ok = received_state(state) }
|
||||
received(result: other) (ok: Bool) recover("test WebSocket receive") { ok = false }
|
||||
received_state(state: dever.websocket.ReceiveState.Read(message)) (ok: Bool) { ok = message_ok(message) }
|
||||
received_state(state: other) (ok: Bool) { ok = false }
|
||||
message_ok(message: dever.websocket.Message.Text(text)) (ok: Bool) { ok = text == "native" }
|
||||
message_ok(message: other) (ok: Bool) { ok = false }
|
||||
connected(result: dever.websocket.ConnectResult.Connected(socket)) (ok: Bool) recover("test WebSocket client") {
|
||||
dever.websocket.send(socket, dever.websocket.Message.Text("native"))
|
||||
ok = received(result(dever.websocket.receive(socket)))
|
||||
dever.websocket.close(socket, 1000, "done")
|
||||
}
|
||||
connected(result: dever.websocket.ConnectResult.Failed(message)) (ok: Bool) recover("test WebSocket client") { ok = false }
|
||||
buffered(result: dever.http.ResponseResult.Read(response), expected: Text) (ok: Bool) recover("test buffered client") {
|
||||
ok = response.status == 200 and response.body == dever.bytes.from_text(expected)
|
||||
}
|
||||
buffered(result: dever.http.ResponseResult.Failed(message), expected: Text) (ok: Bool) recover("test buffered client") { ok = false }
|
||||
chunk(event: dever.io.ReadEvent.Chunk(bytes), state: Bytes) (result: Bytes) recover("test stream failure") { result = dever.bytes.concat(state, bytes) }
|
||||
chunk(event: dever.io.ReadEvent.Failed(message), state: Bytes) (result: Bytes) recover("test stream failure") { result = dever.bytes.from_text("stream failed") }
|
||||
streamed(result: dever.http.StreamResponseResult.Read(response), expected: Text) (ok: Bool) recover("test streamed client") {
|
||||
body = reduce(chunk, response.body, dever.bytes.from_text(""))
|
||||
ok = response.status == 200 and body == dever.bytes.from_text(expected)
|
||||
}
|
||||
streamed(result: dever.http.StreamResponseResult.Failed(message), expected: Text) (ok: Bool) recover("test streamed client") { ok = false }
|
||||
add(value: Int, state: Int) (sum: Int) { sum = state + value }
|
||||
stopped() () {}
|
||||
|
||||
ready(result: dever.http.ClientResult.Ready(client), listener: dever.system.Listener, identity: dever.system.ServerTls, trust: dever.system.ClientTls, port: Int) (ok: Bool) recover("test client lifecycle") {
|
||||
hits = channel(Int, 16)
|
||||
context = Context { prefix = "native"
|
||||
client = client
|
||||
hits = hits }
|
||||
server = run(serve(listener, identity, context))
|
||||
proxy = buffered(result(dever.http.request(client, request("/proxy"))), "native")
|
||||
direct = buffered(result(dever.http.request(client, request("/value"))), "native")
|
||||
upload = dever.http.StreamRequest { method = "POST"
|
||||
target = "/upload"
|
||||
headers = []
|
||||
body = stream([dever.io.ReadEvent.Chunk(dever.bytes.from_text("ab")), dever.io.ReadEvent.Chunk(dever.bytes.from_text("cd"))]) }
|
||||
uploaded = streamed(result(dever.http.open_stream(client, upload)), "abcd")
|
||||
events = streamed(result(dever.http.open(client, request("/events"))), "data: native\n\n")
|
||||
socket = connected(result(dever.websocket.open("wss://localhost:" + int.to_text(port) + "/ws", trust, dever.websocket.default_limits())))
|
||||
dever.net.close_listener(listener)
|
||||
timeout(server, 1000, stopped)
|
||||
dever.http.close_client(client)
|
||||
close(hits)
|
||||
count = reduce(add, stream(hits), 0)
|
||||
ok = proxy and direct and uploaded and events and socket and count == 6
|
||||
}
|
||||
ready(result: dever.http.ClientResult.Failed(message), listener: dever.system.Listener, identity: dever.system.ServerTls, trust: dever.system.ClientTls, port: Int) (ok: Bool) recover("test client lifecycle") { ok = false }
|
||||
bound(result: dever.net.PortResult.Bound(port), listener: dever.system.Listener, identity: dever.system.ServerTls, trust: dever.system.ClientTls) (ok: Bool) recover("test startup") {
|
||||
limits = http_limits()
|
||||
pool = dever.http.default_pool_limits()
|
||||
pool.chunk_bytes = 2
|
||||
ok = ready(result(dever.http.client("https://localhost:" + int.to_text(port), trust, limits, pool)), listener, identity, trust, port)
|
||||
}
|
||||
bound(result: dever.net.PortResult.Failed(message), listener: dever.system.Listener, identity: dever.system.ServerTls, trust: dever.system.ClientTls) (ok: Bool) recover("test startup") { ok = false }
|
||||
listening(result: dever.net.ListenResult.Listening(listener), identity: dever.system.ServerTls, trust: dever.system.ClientTls) (ok: Bool) recover("test startup") {
|
||||
ok = bound(result(dever.net.port(listener)), listener, identity, trust)
|
||||
}
|
||||
listening(result: dever.net.ListenResult.Failed(message), identity: dever.system.ServerTls, trust: dever.system.ClientTls) (ok: Bool) recover("test startup") { ok = false }
|
||||
identity(result: dever.tls.ServerResult.Ready(config), trust: dever.system.ClientTls) (ok: Bool) recover("test TLS identity") {
|
||||
ok = listening(result(dever.net.listen("127.0.0.1", 0)), config, trust)
|
||||
}
|
||||
identity(result: dever.tls.ServerResult.Failed(message), trust: dever.system.ClientTls) (ok: Bool) recover("test TLS identity") { ok = false }
|
||||
trusted(result: dever.tls.ClientResult.Ready(config)) (ok: Bool) recover("test TLS trust") {
|
||||
ok = identity(result(dever.tls.server(dever.bytes.from_text(CERT_PEM), dever.bytes.from_text(KEY_PEM))), config)
|
||||
}
|
||||
trusted(result: dever.tls.ClientResult.Failed(message)) (ok: Bool) recover("test TLS trust") { ok = false }
|
||||
public main() (ok: Bool) { ok = trusted(result(dever.tls.client(dever.bytes.from_text(ROOT_PEM)))) }
|
||||
"#;
|
||||
let mut program = program
|
||||
.replace("CERT_PEM", &format!("{cert:?}"))
|
||||
.replace("KEY_PEM", &format!("{key:?}"))
|
||||
.replace("ROOT_PEM", &format!("{root:?}"));
|
||||
let (connections, protocol) = if http2 {
|
||||
(1, "dever.http.default_http2_limits()")
|
||||
} else {
|
||||
(2, "null")
|
||||
};
|
||||
program.push_str(&format!(
|
||||
r#"
|
||||
http_limits() (limits: dever.http.Limits) {{
|
||||
limits = dever.http.default_limits()
|
||||
limits.connections = {connections}
|
||||
limits.http2 = {protocol}
|
||||
}}
|
||||
"#
|
||||
));
|
||||
if http2 {
|
||||
// 嵌套 /proxy -> /value 必须复用唯一连接;普通 WSS 另由 HTTP/1 变体覆盖。
|
||||
program = program.replace(
|
||||
"socket = connected(result(dever.websocket.open(\"wss://localhost:\" + int.to_text(port) + \"/ws\", trust, dever.websocket.default_limits())))",
|
||||
"socket = true",
|
||||
).replace("count == 6", "count == 5");
|
||||
}
|
||||
program
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_source_checks_and_formats_with_tls_resources_and_streams() {
|
||||
for http2 in [false, true] {
|
||||
let map = sources(&native_source(http2));
|
||||
checked(&map);
|
||||
let formatted = dever_core::format::format(&map.files()[0]).unwrap();
|
||||
let map = sources(&formatted);
|
||||
checked(&map);
|
||||
assert_eq!(
|
||||
dever_core::format::format(&map.files()[0]).unwrap(),
|
||||
formatted
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_tls_pool_context_upload_sse_websocket_and_graceful_shutdown() {
|
||||
assert_eq!(stdout(&native_source(false)), "ok = true\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_http2_tls_multiplexed_proxy_upload_sse_and_child_tasks() {
|
||||
assert_eq!(stdout(&native_source(true)), "ok = true\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backend_example_checks_and_formats() {
|
||||
let map = sources(include_str!(
|
||||
"../../../examples/old/dever/backend/module/main.dever"
|
||||
));
|
||||
checked(&map);
|
||||
let first = dever_core::format::format(&map.files()[0]).unwrap();
|
||||
let map = sources(&first);
|
||||
checked(&map);
|
||||
assert_eq!(dever_core::format::format(&map.files()[0]).unwrap(), first);
|
||||
}
|
||||
Reference in new issue
Block a user