(** Unit tests for Day10_lib.Run_log module. *) let test_dir = ref "" let setup () = let dir = Filename.temp_dir "test-run-log-" "" in test_dir := dir; Day10_lib.Run_log.set_log_base_dir dir; dir let teardown () = if !test_dir <> "" then begin ignore (Sys.command (Printf.sprintf "rm -rf %s" !test_dir)); test_dir := "" end let file_exists path = Sys.file_exists path let read_file path = In_channel.with_open_text path In_channel.input_all (** Test: start_run creates directory structure *) let test_start_run_creates_dirs () = let base_dir = setup () in let run_info = Day10_lib.Run_log.start_run () in (* Check runs directory exists *) assert (file_exists (Filename.concat base_dir "runs")); (* Check run-specific directories exist *) let runs_dir = Filename.concat base_dir "runs" in let entries = Sys.readdir runs_dir in assert (Array.length entries = 1); let run_dir = Filename.concat runs_dir entries.(0) in assert (file_exists (Filename.concat run_dir "build")); assert (file_exists (Filename.concat run_dir "docs")); ignore run_info; teardown (); Printf.printf "PASS: test_start_run_creates_dirs\n%!" (** Test: finish_run creates summary.json *) let test_finish_run_creates_summary () = let base_dir = setup () in let run_info = Day10_lib.Run_log.start_run () in let summary = Day10_lib.Run_log.finish_run run_info ~targets_requested:10 ~solutions_found:8 ~build_success:7 ~build_failed:1 ~doc_success:5 ~doc_failed:1 ~doc_skipped:1 ~failures:[("bad-pkg.1.0.0", "build failed")] in (* Check summary.json exists *) let runs_dir = Filename.concat base_dir "runs" in let entries = Sys.readdir runs_dir in let run_dir = Filename.concat runs_dir entries.(0) in let summary_file = Filename.concat run_dir "summary.json" in assert (file_exists summary_file); (* Check summary content *) let content = read_file summary_file in assert (String.length content > 0); let json = Yojson.Safe.from_string content in let open Yojson.Safe.Util in assert (json |> member "targets_requested" |> to_int = 10); assert (json |> member "build_success" |> to_int = 7); assert (json |> member "build_failed" |> to_int = 1); ignore summary; teardown (); Printf.printf "PASS: test_finish_run_creates_summary\n%!" (** Test: finish_run creates latest symlink *) let test_finish_run_creates_latest () = let base_dir = setup () in let run_info = Day10_lib.Run_log.start_run () in let _ = Day10_lib.Run_log.finish_run run_info ~targets_requested:1 ~solutions_found:1 ~build_success:1 ~build_failed:0 ~doc_success:1 ~doc_failed:0 ~doc_skipped:0 ~failures:[] in (* Check latest symlink exists *) let latest = Filename.concat base_dir "latest" in assert (file_exists latest); (* Check it's a symlink *) let stats = Unix.lstat latest in assert (stats.Unix.st_kind = Unix.S_LNK); teardown (); Printf.printf "PASS: test_finish_run_creates_latest\n%!" (** Test: add_build_log creates symlink *) let test_add_build_log () = let base_dir = setup () in let run_info = Day10_lib.Run_log.start_run () in (* Create a source log file *) let source_log = Filename.concat base_dir "source-build.log" in Out_channel.with_open_text source_log (fun oc -> Out_channel.output_string oc "Build output here\n"); Day10_lib.Run_log.add_build_log run_info ~package:"test-pkg.1.0.0" ~source_log; (* Check log was linked/copied *) let runs_dir = Filename.concat base_dir "runs" in let entries = Sys.readdir runs_dir in let run_dir = Filename.concat runs_dir entries.(0) in let dest_log = Filename.concat (Filename.concat run_dir "build") "test-pkg.1.0.0.log" in assert (file_exists dest_log); teardown (); Printf.printf "PASS: test_add_build_log\n%!" (** Test: add_doc_log creates symlink *) let test_add_doc_log () = let base_dir = setup () in let run_info = Day10_lib.Run_log.start_run () in (* Create a source log file *) let source_log = Filename.concat base_dir "source-doc.log" in Out_channel.with_open_text source_log (fun oc -> Out_channel.output_string oc "Doc output here\n"); Day10_lib.Run_log.add_doc_log run_info ~package:"test-pkg.1.0.0" ~source_log (); (* Check log was linked/copied *) let runs_dir = Filename.concat base_dir "runs" in let entries = Sys.readdir runs_dir in let run_dir = Filename.concat runs_dir entries.(0) in let dest_log = Filename.concat (Filename.concat run_dir "docs") "test-pkg.1.0.0.log" in assert (file_exists dest_log); teardown (); Printf.printf "PASS: test_add_doc_log\n%!" (** Test: summary_to_json produces valid JSON *) let test_summary_to_json () = let summary : Day10_lib.Run_log.summary = { run_id = "2026-02-04-120000"; start_time = "2026-02-04T12:00:00"; end_time = "2026-02-04T12:30:00"; duration_seconds = 1800.0; targets_requested = 100; solutions_found = 95; build_success = 90; build_failed = 5; doc_success = 80; doc_failed = 5; doc_skipped = 5; failures = [("pkg1.1.0", "error1"); ("pkg2.2.0", "error2")]; } in let json = Day10_lib.Run_log.summary_to_json summary in let str = Yojson.Safe.to_string json in assert (String.length str > 0); (* Parse back and verify *) let parsed = Yojson.Safe.from_string str in let open Yojson.Safe.Util in assert (parsed |> member "run_id" |> to_string = "2026-02-04-120000"); assert (parsed |> member "targets_requested" |> to_int = 100); assert (parsed |> member "failures" |> to_list |> List.length = 2); Printf.printf "PASS: test_summary_to_json\n%!" let () = Printf.printf "Running Run_log tests...\n%!"; test_start_run_creates_dirs (); test_finish_run_creates_summary (); test_finish_run_creates_latest (); test_add_build_log (); test_add_doc_log (); test_summary_to_json (); Printf.printf "\nAll Run_log tests passed!\n%!"