Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,28 @@
|
||||
# x.async integration tests
|
||||
|
||||
This folder contains module-oriented integration tests for `x.async`.
|
||||
|
||||
Tests are validation-first programs. Unlike the examples, they are written to
|
||||
assert edge cases, error propagation, cancellation behavior, backpressure, and
|
||||
module integration boundaries. They should fail when a regression is introduced.
|
||||
|
||||
The tests are deliberately local and synthetic. They verify that `x.async`
|
||||
composes cleanly with selected V modules without modifying those modules and
|
||||
without depending on public network services, fixed ports, filesystem paths, or
|
||||
fragile server shutdown behavior.
|
||||
|
||||
Current subfolders:
|
||||
|
||||
- `net_http/`: in-memory `net.http` request/response work through `Pool`.
|
||||
- `net_websocket/`: in-memory `websocket.Message` processing and callback-style
|
||||
error propagation through `Task` and `Group`.
|
||||
- `mcp/`: in-memory MCP JSON-RPC request/response dispatch through `Task`.
|
||||
- `veb/`: in-memory `veb.Context` response lifecycle through `with_timeout()`.
|
||||
|
||||
Run through the guarded validation path:
|
||||
|
||||
```sh
|
||||
sh vlib/x/async/tools/validate.sh
|
||||
```
|
||||
|
||||
The validation script uses isolated `VTMP` and `VCACHE` and runs serially.
|
||||
@@ -0,0 +1,11 @@
|
||||
# mcp integration tests
|
||||
|
||||
These tests keep MCP traffic in memory. They do not start stdio, HTTP, a child
|
||||
process, or any external MCP service.
|
||||
|
||||
The coverage target is the `x.async` boundary around MCP-shaped work:
|
||||
|
||||
- build an MCP request;
|
||||
- decode and validate it inside a `Task`;
|
||||
- produce an MCP response;
|
||||
- decode the result after `Task.wait()`.
|
||||
@@ -0,0 +1,28 @@
|
||||
import context
|
||||
import mcp
|
||||
import x.async as xasync
|
||||
|
||||
struct TestEchoArgs {
|
||||
pub:
|
||||
text string
|
||||
}
|
||||
|
||||
fn test_mcp_task_dispatches_in_memory_request() {
|
||||
request := mcp.new_request(1, 'tools/call', TestEchoArgs{
|
||||
text: 'hello'
|
||||
})
|
||||
mut task := xasync.run[mcp.Response](fn [request] (mut ctx context.Context) !mcp.Response {
|
||||
_ = ctx
|
||||
req := mcp.decode_request(request.encode())!
|
||||
if req.method != 'tools/call' {
|
||||
return error('unexpected MCP method')
|
||||
}
|
||||
args := req.decode_params[TestEchoArgs]()!
|
||||
result := mcp.tool_text_result(args.text)
|
||||
return mcp.new_response(1, result, mcp.ResponseError{})
|
||||
})!
|
||||
|
||||
response := task.wait()!
|
||||
result := response.decode_result[mcp.ToolResult]()!
|
||||
assert result.content.contains('hello')
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# net.http integration tests
|
||||
|
||||
These tests use `net.http` request and response values in memory. They do not
|
||||
open a listener, bind a port, or call an external service.
|
||||
|
||||
The coverage target is the `x.async` boundary around HTTP-shaped work:
|
||||
|
||||
- bounded pool submission;
|
||||
- explicit queue-full backpressure;
|
||||
- draining accepted request handlers through `Pool.close()`;
|
||||
- preserving response data produced by accepted jobs.
|
||||
@@ -0,0 +1,73 @@
|
||||
import context
|
||||
import net.http
|
||||
import time
|
||||
import x.async as xasync
|
||||
|
||||
fn test_net_http_pool_handles_synthetic_requests_with_backpressure() {
|
||||
mut pool := xasync.new_pool(workers: 1, queue_size: 1)!
|
||||
started := chan bool{cap: 1}
|
||||
release := chan bool{cap: 2}
|
||||
responses := chan int{cap: 2}
|
||||
first_req := http.new_request(.get, '/first', '')
|
||||
second_req := http.new_request(.get, '/second', '')
|
||||
third_req := http.new_request(.get, '/third', '')
|
||||
|
||||
pool.try_submit(fn [first_req, started, release, responses] (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
started <- true
|
||||
_ := <-release
|
||||
resp := synthetic_http_response(first_req)!
|
||||
responses <- resp.status_code
|
||||
})!
|
||||
wait_for_http_signal(started, 'first synthetic HTTP job did not start')
|
||||
|
||||
pool.try_submit(fn [second_req, release, responses] (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
_ := <-release
|
||||
resp := synthetic_http_response(second_req)!
|
||||
responses <- resp.status_code
|
||||
})!
|
||||
pool.try_submit(fn [third_req] (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
_ = third_req
|
||||
}) or {
|
||||
assert err.msg() == 'async: pool queue is full'
|
||||
release <- true
|
||||
release <- true
|
||||
pool.close()!
|
||||
assert read_http_status(responses) == 200
|
||||
assert read_http_status(responses) == 200
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn synthetic_http_response(req http.Request) !http.Response {
|
||||
if req.url == '' {
|
||||
return error('empty synthetic HTTP URL')
|
||||
}
|
||||
return http.new_response(status: .ok, body: req.url)
|
||||
}
|
||||
|
||||
fn wait_for_http_signal(signal chan bool, message string) {
|
||||
select {
|
||||
ok := <-signal {
|
||||
assert ok
|
||||
}
|
||||
1 * time.second {
|
||||
assert false, message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_http_status(responses chan int) int {
|
||||
select {
|
||||
status := <-responses {
|
||||
return status
|
||||
}
|
||||
1 * time.second {
|
||||
assert false, 'synthetic HTTP response was not produced'
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# net.websocket integration tests
|
||||
|
||||
These tests are intentionally in-memory and synthetic. They exercise
|
||||
`websocket.Message` processing and callback-shaped control flow with `x.async`,
|
||||
but they do not start a websocket server or claim end-to-end websocket server
|
||||
coverage.
|
||||
|
||||
That limitation is important: in this V snapshot, `websocket.Server.close()` is
|
||||
not a complete, stable shutdown primitive for a fragile validation test. The
|
||||
tests therefore avoid binding ports and focus on the part `x.async` can safely
|
||||
compose: message work, cancellation, and error propagation around callbacks.
|
||||
@@ -0,0 +1,44 @@
|
||||
import context
|
||||
import net.websocket
|
||||
import x.async as xasync
|
||||
|
||||
fn test_net_websocket_task_processes_message_in_memory() {
|
||||
msg := websocket.Message{
|
||||
opcode: .text_frame
|
||||
payload: 'hello'.bytes()
|
||||
}
|
||||
mut task := xasync.run[string](fn [msg] (mut ctx context.Context) !string {
|
||||
_ = ctx
|
||||
return websocket_message_text(msg)!
|
||||
})!
|
||||
assert task.wait()! == 'hello'
|
||||
}
|
||||
|
||||
fn test_net_websocket_callback_error_propagates_through_group() {
|
||||
mut group := xasync.new_group(context.background())
|
||||
observed := chan string{cap: 1}
|
||||
group.go(fn [observed] (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
msg := websocket.Message{
|
||||
opcode: .close
|
||||
}
|
||||
text := websocket_message_text(msg) or {
|
||||
observed <- err.msg()
|
||||
return err
|
||||
}
|
||||
observed <- text
|
||||
})!
|
||||
group.wait() or {
|
||||
assert err.msg() == 'unsupported websocket opcode'
|
||||
assert <-observed == 'unsupported websocket opcode'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn websocket_message_text(msg websocket.Message) !string {
|
||||
if msg.opcode != .text_frame {
|
||||
return error('unsupported websocket opcode')
|
||||
}
|
||||
return msg.payload.bytestr()
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# veb integration tests
|
||||
|
||||
These tests use `veb.Context` in memory. They do not start a `veb` server, bind
|
||||
a port, or depend on HTTP client/server timing.
|
||||
|
||||
The coverage target is narrow: `x.async` can bound and propagate errors for a
|
||||
synthetic veb handler step without becoming part of `veb`'s runtime or server
|
||||
lifecycle.
|
||||
@@ -0,0 +1,18 @@
|
||||
import context
|
||||
import time
|
||||
import veb
|
||||
import x.async as xasync
|
||||
|
||||
fn test_veb_context_response_inside_timeout() {
|
||||
xasync.with_timeout(1 * time.second, fn (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
mut web_ctx := veb.Context{}
|
||||
_ := web_ctx.text('hello from veb')
|
||||
if web_ctx.res.status_code != 200 {
|
||||
return error('unexpected veb status')
|
||||
}
|
||||
if web_ctx.res.body != 'hello from veb' {
|
||||
return error('unexpected veb body')
|
||||
}
|
||||
})!
|
||||
}
|
||||
Reference in new issue
Block a user