FastCGI implementation in OCaml

Migrate tests from stdlib I/O to Eio-based file operations

Replace blocking stdlib file operations with Eio's capability-based filesystem access and streaming I/O. Tests now use Eio_main.run, Eio.Path.with_open_in, and Eio.Buf_read.of_flow for more idiomatic Eio patterns that eliminate intermediate string buffers.

šŸ¤– Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

+2 -2
test/dune
···
(test
(name simple_test)
-
(libraries fastcgi eio)
+
(libraries fastcgi eio eio_main)
(deps (source_tree test_cases))
)
(test
(name validate_all_test_cases)
-
(libraries fastcgi eio)
+
(libraries fastcgi eio eio_main)
(deps (source_tree test_cases))
)
+14 -17
test/simple_test.ml
···
Printf.printf "āœ“ Long key-value encoding test passed\n%!"
-
let test_with_binary_test_case filename expected_type expected_request_id =
+
let test_with_binary_test_case ~fs filename expected_type expected_request_id =
Printf.printf "Testing with binary test case: %s...\n%!" filename;
-
let content =
-
let ic = open_in_bin ("test_cases/" ^ filename) in
-
let len = in_channel_length ic in
-
let content = really_input_string ic len in
-
close_in ic;
-
content
+
let parsed =
+
Eio.Path.with_open_in (fs, "test_cases/" ^ filename) @@ fun flow ->
+
let buf_read = Eio.Buf_read.of_flow flow ~max_size:1000000 in
+
Record.read buf_read
in
-
-
let buf_read = Eio.Buf_read.of_string content in
-
let parsed = Record.read buf_read in
assert (parsed.version = 1);
assert (parsed.record_type = expected_type);
···
Printf.printf "āœ“ Binary test case %s passed\n%!" filename
-
let run_tests () =
+
let run_tests ~fs =
Printf.printf "Running FastCGI Record tests...\n\n%!";
test_record_types ();
···
test_long_key_value ();
(* Test with some of our binary test cases *)
-
test_with_binary_test_case "begin_request_responder.bin" Begin_request 1;
-
test_with_binary_test_case "params_empty.bin" Params 1;
-
test_with_binary_test_case "end_request_success.bin" End_request 1;
-
test_with_binary_test_case "get_values.bin" Get_values 0;
-
test_with_binary_test_case "abort_request.bin" Abort_request 1;
+
test_with_binary_test_case ~fs "begin_request_responder.bin" Begin_request 1;
+
test_with_binary_test_case ~fs "params_empty.bin" Params 1;
+
test_with_binary_test_case ~fs "end_request_success.bin" End_request 1;
+
test_with_binary_test_case ~fs "get_values.bin" Get_values 0;
+
test_with_binary_test_case ~fs "abort_request.bin" Abort_request 1;
Printf.printf "\nāœ… All FastCGI Record tests passed!\n%!"
-
let () = run_tests ()
+
let () = Eio_main.run @@ fun env ->
+
let fs = Eio.Stdenv.cwd env in
+
run_tests ~fs:(fst fs)
+44 -66
test/validate_all_test_cases.ml
···
("unknown_type.bin", Unknown_type, 0);
]
-
let test_binary_file filename expected_type expected_request_id =
+
let test_binary_file ~fs filename expected_type expected_request_id =
Printf.printf "Testing %s... " filename;
-
let content =
-
let ic = open_in_bin ("test_cases/" ^ filename) in
-
let len = in_channel_length ic in
-
let content = really_input_string ic len in
-
close_in ic;
-
content
+
let parsed =
+
Eio.Path.with_open_in (fs, "test_cases/" ^ filename) @@ fun flow ->
+
let buf_read = Eio.Buf_read.of_flow flow ~max_size:1000000 in
+
Record.read buf_read
in
-
let buf_read = Eio.Buf_read.of_string content in
-
let parsed = Record.read buf_read in
-
assert (parsed.version = 1);
assert (parsed.record_type = expected_type);
assert (parsed.request_id = expected_request_id);
Printf.printf "āœ“\n%!"
-
let test_params_decoding () =
+
let test_params_decoding ~fs =
Printf.printf "Testing params record content decoding... ";
-
let content =
-
let ic = open_in_bin "test_cases/params_get.bin" in
-
let len = in_channel_length ic in
-
let content = really_input_string ic len in
-
close_in ic;
-
content
+
let parsed =
+
Eio.Path.with_open_in (fs, "test_cases/params_get.bin") @@ fun flow ->
+
let buf_read = Eio.Buf_read.of_flow flow ~max_size:1000000 in
+
Record.read buf_read
in
-
let buf_read = Eio.Buf_read.of_string content in
-
let parsed = Record.read buf_read in
-
(* Decode the params content *)
let params = Record.KV.decode parsed.content in
···
Printf.printf "āœ“\n%!"
-
let test_large_record () =
+
let test_large_record ~fs =
Printf.printf "Testing large record... ";
-
let content =
-
let ic = open_in_bin "test_cases/large_record.bin" in
-
let len = in_channel_length ic in
-
let content = really_input_string ic len in
-
close_in ic;
-
content
+
let parsed =
+
Eio.Path.with_open_in (fs, "test_cases/large_record.bin") @@ fun flow ->
+
let buf_read = Eio.Buf_read.of_flow flow ~max_size:1000000 in
+
Record.read buf_read
in
-
let buf_read = Eio.Buf_read.of_string content in
-
let parsed = Record.read buf_read in
-
assert (parsed.version = 1);
assert (parsed.record_type = Stdout);
assert (parsed.request_id = 1);
···
Printf.printf "āœ“\n%!"
-
let test_padded_record () =
+
let test_padded_record ~fs =
Printf.printf "Testing padded record... ";
-
let content =
-
let ic = open_in_bin "test_cases/padded_record.bin" in
-
let len = in_channel_length ic in
-
let content = really_input_string ic len in
-
close_in ic;
-
content
+
let parsed =
+
Eio.Path.with_open_in (fs, "test_cases/padded_record.bin") @@ fun flow ->
+
let buf_read = Eio.Buf_read.of_flow flow ~max_size:1000000 in
+
Record.read buf_read
in
-
-
let buf_read = Eio.Buf_read.of_string content in
-
let parsed = Record.read buf_read in
assert (parsed.version = 1);
assert (parsed.record_type = Stdout);
···
Printf.printf "āœ“\n%!"
-
let test_multiplexed_records () =
+
let test_multiplexed_records ~fs =
Printf.printf "Testing multiplexed records... ";
-
let content =
-
let ic = open_in_bin "test_cases/multiplexed_requests.bin" in
-
let len = in_channel_length ic in
-
let content = really_input_string ic len in
-
close_in ic;
-
content
+
let records =
+
Eio.Path.with_open_in (fs, "test_cases/multiplexed_requests.bin") @@ fun flow ->
+
let buf_read = Eio.Buf_read.of_flow flow ~max_size:1000000 in
+
let records = ref [] in
+
+
(* Read all records from the multiplexed stream *)
+
(try
+
while true do
+
let record = Record.read buf_read in
+
records := record :: !records
+
done
+
with End_of_file -> ());
+
!records
in
-
let buf_read = Eio.Buf_read.of_string content in
-
let records = ref [] in
-
-
(* Read all records from the multiplexed stream *)
-
(try
-
while true do
-
let record = Record.read buf_read in
-
records := record :: !records
-
done
-
with End_of_file -> ());
-
-
let records = List.rev !records in
+
let records = List.rev records in
(* Should have multiple records with different request IDs *)
assert (List.length records > 5);
···
Printf.printf "āœ“\n%!"
-
let run_all_tests () =
+
let run_all_tests ~fs =
Printf.printf "Validating all FastCGI test case files...\n\n%!";
(* Test individual files *)
List.iter (fun (filename, expected_type, expected_request_id) ->
-
test_binary_file filename expected_type expected_request_id
+
test_binary_file ~fs filename expected_type expected_request_id
) test_cases;
Printf.printf "\nTesting specific content decoding...\n%!";
-
test_params_decoding ();
-
test_large_record ();
-
test_padded_record ();
-
test_multiplexed_records ();
+
test_params_decoding ~fs;
+
test_large_record ~fs;
+
test_padded_record ~fs;
+
test_multiplexed_records ~fs;
Printf.printf "\nāœ… All %d test case files validated successfully!\n%!" (List.length test_cases);
Printf.printf "āœ… FastCGI Record implementation is working correctly!\n%!"
-
let () = run_all_tests ()
+
let () = Eio_main.run @@ fun env ->
+
let fs = Eio.Stdenv.cwd env in
+
run_all_tests ~fs:(fst fs)