(** Unit tests for run data reading *) let test_dir = ref "" let setup () = let dir = Filename.temp_dir "test-run-data-" "" in test_dir := dir; dir let teardown () = if !test_dir <> "" then begin ignore (Sys.command (Printf.sprintf "rm -rf %s" !test_dir)); test_dir := "" end let mkdir_p path = let rec create dir = if not (Sys.file_exists dir) then begin create (Filename.dirname dir); try Unix.mkdir dir 0o755 with Unix.Unix_error (Unix.EEXIST, _, _) -> () end in create path let write_file path content = let dir = Filename.dirname path in mkdir_p dir; Out_channel.with_open_text path (fun oc -> Out_channel.output_string oc content) (** Test: list_runs returns runs sorted by most recent first *) let test_list_runs () = let base_dir = setup () in let runs_dir = Filename.concat base_dir "runs" in mkdir_p (Filename.concat runs_dir "2026-02-01-120000"); mkdir_p (Filename.concat runs_dir "2026-02-03-120000"); mkdir_p (Filename.concat runs_dir "2026-02-02-120000"); let runs = Day10_web_data.Run_data.list_runs ~log_dir:base_dir in assert (List.length runs = 3); assert (List.hd runs = "2026-02-03-120000"); teardown (); Printf.printf "PASS: test_list_runs\n%!" (** Test: read_summary parses summary.json *) let test_read_summary () = let base_dir = setup () in let run_dir = Filename.concat (Filename.concat base_dir "runs") "2026-02-04-120000" in mkdir_p run_dir; write_file (Filename.concat run_dir "summary.json") {|{ "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": [{"package": "bad.1.0", "error": "build failed"}] }|}; let summary = Day10_web_data.Run_data.read_summary ~log_dir:base_dir ~run_id:"2026-02-04-120000" in assert (Option.is_some summary); let s = Option.get summary in assert (s.run_id = "2026-02-04-120000"); assert (s.build_success = 90); assert (List.length s.failures = 1); teardown (); Printf.printf "PASS: test_read_summary\n%!" (** Test: read_summary returns None for missing run *) let test_read_summary_missing () = let base_dir = setup () in let summary = Day10_web_data.Run_data.read_summary ~log_dir:base_dir ~run_id:"nonexistent" in assert (Option.is_none summary); teardown (); Printf.printf "PASS: test_read_summary_missing\n%!" (** Test: get_latest_run_id follows symlink *) let test_get_latest_run_id () = let base_dir = setup () in let runs_dir = Filename.concat base_dir "runs" in mkdir_p (Filename.concat runs_dir "2026-02-04-120000"); let latest = Filename.concat base_dir "latest" in Unix.symlink "runs/2026-02-04-120000" latest; let latest_id = Day10_web_data.Run_data.get_latest_run_id ~log_dir:base_dir in assert (Option.is_some latest_id); assert (Option.get latest_id = "2026-02-04-120000"); teardown (); Printf.printf "PASS: test_get_latest_run_id\n%!" (** Test: read_log returns log content *) let test_read_log () = let base_dir = setup () in let run_dir = Filename.concat (Filename.concat base_dir "runs") "2026-02-04-120000" in write_file (Filename.concat (Filename.concat run_dir "build") "test-pkg.1.0.log") "Build output here\n"; let content = Day10_web_data.Run_data.read_build_log ~log_dir:base_dir ~run_id:"2026-02-04-120000" ~package:"test-pkg.1.0" in assert (Option.is_some content); assert (String.trim (Option.get content) = "Build output here"); teardown (); Printf.printf "PASS: test_read_log\n%!" let () = Printf.printf "Running Run_data tests...\n%!"; test_list_runs (); test_read_summary (); test_read_summary_missing (); test_get_latest_run_id (); test_read_log (); Printf.printf "\nAll Run_data tests passed!\n%!"