Something went wrong. Try again.
OpenAPI generator for OCaml with Requests/Eio/Jsont
Something went wrong. Try again.
11 kB · 341 lines
OCaml
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342(** Tests for ocaml-openapi *)
module Spec = Openapi.Specmodule Codegen = Openapi.Codegenmodule Runtime = Openapi.Runtime
(** {1 Path Template Tests} *)
let test_path_render_simple () = let result = Runtime.Path.render ~params:[] "/users" in Alcotest.(check string) "no params" "/users" result
let test_path_render_one_param () = let result = Runtime.Path.render ~params:[("id", "123")] "/users/{id}" in Alcotest.(check string) "one param" "/users/123" result
let test_path_render_multiple_params () = let result = Runtime.Path.render ~params:[("userId", "42"); ("postId", "99")] "/users/{userId}/posts/{postId}" in Alcotest.(check string) "multiple params" "/users/42/posts/99" result
let test_path_parameters () = let params = Runtime.Path.parameters "/users/{userId}/posts/{postId}" in Alcotest.(check (list string)) "extract params" ["userId"; "postId"] params
(** {1 Query Parameter Tests} *)
let test_query_singleton () = let params = Runtime.Query.singleton ~key:"name" ~value:"alice" in Alcotest.(check (list (pair string string))) "singleton" [("name", "alice")] params
let test_query_optional_some () = let params = Runtime.Query.optional ~key:"name" ~value:(Some "alice") in Alcotest.(check (list (pair string string))) "optional some" [("name", "alice")] params
let test_query_optional_none () = let params = Runtime.Query.optional ~key:"name" ~value:None in Alcotest.(check (list (pair string string))) "optional none" [] params
let test_query_encode_empty () = let result = Runtime.Query.encode [] in Alcotest.(check string) "empty query" "" result
let test_query_encode_single () = let result = Runtime.Query.encode [("name", "alice")] in Alcotest.(check string) "single query" "?name=alice" result
let test_query_encode_multiple () = let result = Runtime.Query.encode [("name", "alice"); ("age", "30")] in Alcotest.(check string) "multiple query" "?name=alice&age=30" result
let test_query_encode_special_chars () = let result = Runtime.Query.encode [("q", "hello world")] in Alcotest.(check string) "special chars" "?q=hello%20world" result
(** {1 Name Conversion Tests} *)
let test_snake_case_simple () = let result = Codegen.Name.to_snake_case "getUserById" in Alcotest.(check string) "camel to snake" "get_user_by_id" result
let test_snake_case_with_dashes () = let result = Codegen.Name.to_snake_case "user-name" in Alcotest.(check string) "dashes to underscore" "user_name" result
let test_snake_case_reserved () = let result = Codegen.Name.to_snake_case "type" in Alcotest.(check string) "reserved word" "type_" result
let test_module_name () = let result = Codegen.Name.to_module_name "user_profile" in Alcotest.(check string) "module name" "UserProfile" result
let test_variant_name () = let result = Codegen.Name.to_variant_name "active_user" in Alcotest.(check string) "variant name" "Active_user" result
(** {1 Spec Parsing Tests} *)
let minimal_spec = {|{ "openapi": "3.0.0", "info": { "title": "Test API", "version": "1.0.0" }, "paths": {}}|}
let test_parse_minimal_spec () = match Spec.of_string minimal_spec with | Error e -> Alcotest.fail e | Ok spec -> Alcotest.(check string) "openapi version" "3.0.0" spec.openapi; Alcotest.(check string) "title" "Test API" spec.info.title; Alcotest.(check string) "version" "1.0.0" spec.info.version
let spec_with_schema = {|{ "openapi": "3.0.0", "info": { "title": "Test API", "version": "1.0.0" }, "paths": {}, "components": { "schemas": { "User": { "type": "object", "properties": { "id": { "type": "integer" }, "name": { "type": "string" }, "email": { "type": "string", "format": "email" } }, "required": ["id", "name"] } } }}|}
let test_parse_schema () = match Spec.of_string spec_with_schema with | Error e -> Alcotest.fail e | Ok spec -> match spec.components with | None -> Alcotest.fail "expected components" | Some c -> Alcotest.(check int) "schema count" 1 (List.length c.schemas); match List.assoc_opt "User" c.schemas with | None -> Alcotest.fail "expected User schema" | Some (Spec.Ref _) -> Alcotest.fail "expected value not ref" | Some (Spec.Value s) -> Alcotest.(check (option string)) "type" (Some "object") s.type_; Alcotest.(check int) "properties" 3 (List.length s.properties); Alcotest.(check (list string)) "required" ["id"; "name"] s.required
let spec_with_enum = {|{ "openapi": "3.0.0", "info": { "title": "Test API", "version": "1.0.0" }, "paths": {}, "components": { "schemas": { "Status": { "type": "string", "enum": ["active", "inactive", "pending"] } } }}|}
let test_parse_enum () = match Spec.of_string spec_with_enum with | Error e -> Alcotest.fail e | Ok spec -> match spec.components with | None -> Alcotest.fail "expected components" | Some c -> match List.assoc_opt "Status" c.schemas with | None -> Alcotest.fail "expected Status schema" | Some (Spec.Ref _) -> Alcotest.fail "expected value not ref" | Some (Spec.Value s) -> match s.enum with | None -> Alcotest.fail "expected enum" | Some values -> Alcotest.(check int) "enum count" 3 (List.length values)
let spec_with_paths = {|{ "openapi": "3.0.0", "info": { "title": "Test API", "version": "1.0.0" }, "paths": { "/users": { "get": { "operationId": "listUsers", "summary": "List all users", "responses": { "200": { "description": "Success" } } }, "post": { "operationId": "createUser", "summary": "Create a user", "responses": { "201": { "description": "Created" } } } }, "/users/{id}": { "get": { "operationId": "getUser", "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } } ], "responses": { "200": { "description": "Success" } } } } }}|}
let test_parse_paths () = match Spec.of_string spec_with_paths with | Error e -> Alcotest.fail e | Ok spec -> Alcotest.(check int) "path count" 2 (List.length spec.paths); match List.assoc_opt "/users" spec.paths with | None -> Alcotest.fail "expected /users path" | Some path_item -> (match path_item.get with | None -> Alcotest.fail "expected GET" | Some op -> Alcotest.(check (option string)) "operation id" (Some "listUsers") op.operation_id); (match path_item.post with | None -> Alcotest.fail "expected POST" | Some op -> Alcotest.(check (option string)) "operation id" (Some "createUser") op.operation_id)
(** {1 Code Generation Tests} *)
let contains_substring s sub = let len_s = String.length s in let len_sub = String.length sub in if len_sub > len_s then false else let rec check i = if i > len_s - len_sub then false else if String.sub s i len_sub = sub then true else check (i + 1) in check 0
let test_split_schema_name () = let p, s = Codegen.Name.split_schema_name "AlbumResponseDto" in Alcotest.(check string) "prefix" "Album" p; Alcotest.(check string) "suffix" "ResponseDto" s
let test_split_schema_name_no_suffix () = let p, s = Codegen.Name.split_schema_name "User" in Alcotest.(check string) "prefix" "User" p; Alcotest.(check string) "suffix" "T" s
let test_generate_files () = match Spec.of_string spec_with_schema with | Error e -> Alcotest.fail e | Ok spec -> let config = Codegen.{ output_dir = "."; package_name = "test_api"; spec_path = None; } in let files = Codegen.generate ~config spec in Alcotest.(check int) "file count" 4 (List.length files); let ml = List.assoc_opt "test_api.ml" files in (match ml with | None -> Alcotest.fail "missing .ml file" | Some content -> Alcotest.(check bool) "contains module User" true (contains_substring content "module User"))
let test_generate_enum_schema () = match Spec.of_string spec_with_enum with | Error e -> Alcotest.fail e | Ok spec -> let config = Codegen.{ output_dir = "."; package_name = "test_enum"; spec_path = None; } in let files = Codegen.generate ~config spec in let ml = List.assoc_opt "test_enum.ml" files in (match ml with | None -> Alcotest.fail "missing .ml file" | Some content -> Alcotest.(check bool) "contains Active variant" true (contains_substring content "Active"))
(** {1 Test Suites} *)
let path_tests = [ "render simple", `Quick, test_path_render_simple; "render one param", `Quick, test_path_render_one_param; "render multiple params", `Quick, test_path_render_multiple_params; "extract parameters", `Quick, test_path_parameters;]
let query_tests = [ "singleton", `Quick, test_query_singleton; "optional some", `Quick, test_query_optional_some; "optional none", `Quick, test_query_optional_none; "encode empty", `Quick, test_query_encode_empty; "encode single", `Quick, test_query_encode_single; "encode multiple", `Quick, test_query_encode_multiple; "encode special chars", `Quick, test_query_encode_special_chars;]
let name_tests = [ "snake case simple", `Quick, test_snake_case_simple; "snake case dashes", `Quick, test_snake_case_with_dashes; "snake case reserved", `Quick, test_snake_case_reserved; "module name", `Quick, test_module_name; "variant name", `Quick, test_variant_name;]
let spec_tests = [ "parse minimal", `Quick, test_parse_minimal_spec; "parse schema", `Quick, test_parse_schema; "parse enum", `Quick, test_parse_enum; "parse paths", `Quick, test_parse_paths;]
let codegen_tests = [ "split schema name", `Quick, test_split_schema_name; "split schema name no suffix", `Quick, test_split_schema_name_no_suffix; "generate files", `Quick, test_generate_files; "generate enum schema", `Quick, test_generate_enum_schema;]
let () = Alcotest.run "openapi" [ "Path", path_tests; "Query", query_tests; "Name", name_tests; "Spec", spec_tests; "Codegen", codegen_tests; ]