View raw

1 (** Configuration loading: round-tripping, backward compatibility, and rejection 2 of malformed files and out-of-range values. *) 3 4 open Test_helpers 5 6 let test_round_trip () = 7 with_temp_file "ogit" ".toml" (fun file -> 8 let config = 9 Ogit.Config. 10 { 11 user_name = "alice"; 12 default_branch = "main"; 13 git_project_root = "/srv/git"; 14 commits_max_displayed = 25; 15 root_title = ""; 16 nav_logo = "/branding/logo.svg"; 17 host = "127.0.0.1"; 18 port = 9000; 19 favorite_repositories = []; 20 archived_repositories = []; 21 } 22 in 23 Ogit.Config.write_file ~file (Ogit.Config.to_table config); 24 match Ogit.Config.read_file ~file () with 25 | Ok config' -> 26 Alcotest.(check string) "user_name" config.user_name config'.user_name; 27 Alcotest.(check string) 28 "branch" config.default_branch config'.default_branch; 29 Alcotest.(check string) 30 "root" config.git_project_root config'.git_project_root; 31 Alcotest.(check int) 32 "commits" config.commits_max_displayed config'.commits_max_displayed; 33 Alcotest.(check string) "host" config.host config'.host; 34 Alcotest.(check string) "nav_logo" config.nav_logo config'.nav_logo; 35 Alcotest.(check int) "port" config.port config'.port 36 | Error error -> fail_config_error error) 37 38 let test_backward_compat () = 39 with_temp_file "ogit" ".toml" (fun file -> 40 Out_channel.with_open_text file (fun channel -> 41 Printf.fprintf channel 42 "default_branch = \"main\"\n\ 43 git_project_root = \"/srv/git\"\n\ 44 commits_max_displayed = 10\n\ 45 title = \"Legacy repositories\"\n"); 46 match Ogit.Config.read_file ~file () with 47 | Ok config -> 48 Alcotest.(check string) "default user_name" "" config.user_name; 49 Alcotest.(check string) 50 "legacy title" "Legacy repositories" config.root_title; 51 Alcotest.(check string) 52 "default nav logo" "/static/git_icon.svg" config.nav_logo; 53 Alcotest.(check string) "default host" "127.0.0.1" config.host; 54 Alcotest.(check int) "default port" 8081 config.port 55 | Error error -> fail_config_error error) 56 57 (* Every key has a default, so a file overriding a single setting is valid; 58 the untouched settings keep their defaults. *) 59 let test_partial_file () = 60 with_temp_file "ogit-partial" ".toml" (fun file -> 61 Out_channel.with_open_text file (fun channel -> 62 output_string channel "port = 9999\n"); 63 match Ogit.Config.read_file ~file () with 64 | Ok config -> 65 Alcotest.(check int) "overridden port" 9999 config.port; 66 Alcotest.(check string) 67 "default root" Ogit.Config.default.git_project_root 68 config.git_project_root; 69 Alcotest.(check string) 70 "default branch" Ogit.Config.default.default_branch 71 config.default_branch; 72 Alcotest.(check int) 73 "default commits" Ogit.Config.default.commits_max_displayed 74 config.commits_max_displayed 75 | Error error -> fail_config_error error) 76 77 (* A key that is present with the wrong type is still an error: only absence 78 falls back to the default. *) 79 let test_mistyped_key () = 80 with_temp_file "ogit-mistyped" ".toml" (fun file -> 81 Out_channel.with_open_text file (fun channel -> 82 output_string channel "git_project_root = 5\n"); 83 match Ogit.Config.read_file ~file () with 84 | Error (Ogit.Config.Invalid_value _) -> () 85 | Error error -> fail_config_error error 86 | Ok _ -> Alcotest.fail "mistyped key was accepted") 87 88 let test_malformed () = 89 with_temp_file "ogit-malformed" ".toml" (fun file -> 90 Out_channel.with_open_text file (fun channel -> 91 output_string channel "user = [\n"); 92 match Ogit.Config.read_file ~file () with 93 | Error (Ogit.Config.Parse_error _) -> () 94 | Error error -> fail_config_error error 95 | Ok _ -> Alcotest.fail "malformed configuration was accepted") 96 97 let test_invalid_value () = 98 with_temp_file "ogit-invalid" ".toml" (fun file -> 99 let invalid = Ogit.Config.{ default with commits_max_displayed = 0 } in 100 Ogit.Config.write_file ~file (Ogit.Config.to_table invalid); 101 match Ogit.Config.read_file ~file () with 102 | Error (Ogit.Config.Invalid_value _) -> () 103 | Error error -> fail_config_error error 104 | Ok _ -> Alcotest.fail "invalid commit limit was accepted") 105 106 let test_invalid_port_high () = 107 with_temp_file "ogit-port-high" ".toml" (fun file -> 108 let invalid = Ogit.Config.{ default with port = 65536 } in 109 Ogit.Config.write_file ~file (Ogit.Config.to_table invalid); 110 match Ogit.Config.read_file ~file () with 111 | Error (Ogit.Config.Invalid_value _) -> () 112 | Error error -> fail_config_error error 113 | Ok _ -> Alcotest.fail "port 65536 was accepted") 114 115 let test_invalid_port_zero () = 116 with_temp_file "ogit-port-zero" ".toml" (fun file -> 117 let invalid = Ogit.Config.{ default with port = 0 } in 118 Ogit.Config.write_file ~file (Ogit.Config.to_table invalid); 119 match Ogit.Config.read_file ~file () with 120 | Error (Ogit.Config.Invalid_value _) -> () 121 | Error error -> fail_config_error error 122 | Ok _ -> Alcotest.fail "port 0 was accepted") 123 124 let test_directory_rejected () = 125 with_temp_directory "ogit-config-directory" (fun directory -> 126 match Ogit.Config.read_file ~file:directory () with 127 | Error (Ogit.Config.Io_error _) -> () 128 | Error error -> fail_config_error error 129 | Ok _ -> Alcotest.fail "a directory was accepted as a config file") 130 131 let test_invalid_implicit () = 132 with_temp_directory "ogit-xdg" (fun config_home -> 133 let directory = Filename.concat config_home "ogit" in 134 Unix.mkdir directory 0o755; 135 let file = Filename.concat directory "config.toml" in 136 Out_channel.with_open_text file (fun channel -> 137 output_string channel "user = [\n"); 138 with_environment "OGIT_CONFIG" "" (fun () -> 139 with_environment "XDG_CONFIG_HOME" config_home (fun () -> 140 match Ogit.Config.load () with 141 | Error (Ogit.Config.Parse_error _) -> () 142 | Error error -> fail_config_error error 143 | Ok _ -> Alcotest.fail "invalid implicit config should fail"))) 144 145 let test_explicit_missing () = 146 let missing = Filename.temp_file "ogit-missing" ".toml" in 147 Sys.remove missing; 148 with_environment "OGIT_CONFIG" missing (fun () -> 149 match Ogit.Config.load () with 150 | Error (Ogit.Config.Not_found file) -> 151 Alcotest.(check string) "path" missing file 152 | Error error -> fail_config_error error 153 | Ok _ -> Alcotest.fail "explicitly missing config should fail") 154 155 let test_location () = 156 with_environment "OGIT_CONFIG" "" (fun () -> 157 with_environment "XDG_CONFIG_HOME" "/tmp/xdg-config" (fun () -> 158 let expected = 159 Filename.concat 160 (Filename.concat "/tmp/xdg-config" "ogit") 161 "config.toml" 162 in 163 Alcotest.(check string) 164 "XDG path" expected 165 (Ogit.Config.locate_config_file ()))); 166 with_environment "OGIT_CONFIG" "/tmp/custom-ogit.toml" (fun () -> 167 Alcotest.(check string) 168 "explicit path" "/tmp/custom-ogit.toml" 169 (Ogit.Config.locate_config_file ())) 170 171 let suite = 172 ( "config", 173 [ 174 Alcotest.test_case "round trip" `Quick test_round_trip; 175 Alcotest.test_case "backward compat" `Quick test_backward_compat; 176 Alcotest.test_case "partial file" `Quick test_partial_file; 177 Alcotest.test_case "mistyped key" `Quick test_mistyped_key; 178 Alcotest.test_case "malformed" `Quick test_malformed; 179 Alcotest.test_case "invalid value" `Quick test_invalid_value; 180 Alcotest.test_case "port too high" `Quick test_invalid_port_high; 181 Alcotest.test_case "port zero" `Quick test_invalid_port_zero; 182 Alcotest.test_case "directory rejected" `Quick test_directory_rejected; 183 Alcotest.test_case "invalid implicit" `Quick test_invalid_implicit; 184 Alcotest.test_case "explicit missing" `Quick test_explicit_missing; 185 Alcotest.test_case "location" `Quick test_location; 186 ] ) 187