Skip to content

Commit 31f10c3

Browse files
neuralsorcerermeta-codesync[bot]
authored andcommitted
Add CLI tests (#360)
Summary: Pull Request resolved: #360 Differential Revision: D95815944 Pulled By: talgalili fbshipit-source-id: fd01f319bec419203668477e43b531cdec8f28b7
1 parent bbd59d2 commit 31f10c3

3 files changed

Lines changed: 170 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@
9696
- Added focused tests for empty threshold iterables, mixed `None` threshold groups in dict mode, and explicit all-`None` threshold handling across return formats.
9797
- **Expanded IPW diagnostics coverage for fit-parameter reporting**
9898
- Refactored diagnostics tests to use a shared IPW setup helper (removing repeated fixture construction), added edge-case assertions for filtered non-string solver/penalty values and NaN coercion of non-scalar `tol`/`l1_ratio` inputs, and now assert solver/penalty labels match fitted model parameters.
99+
- **Added unit coverage for CLI I/O and empty-batch handling**
100+
- Added focused tests for `BalanceCLI.process_batch()` empty-sample failure payloads, `load_and_check_input()` CSV loading paths, and `write_outputs()` delimiter-aware output writing for both adjusted and diagnostics files.
99101

100102
# 0.16.0 (2026-02-09)
101103

balance/cli.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,6 @@ def process_batch(
667667
set(result.keys()) == {"adjusted", "diagnostics"}
668668
# True
669669
"""
670-
# TODO: add unit tests
671670
sample_df, target_df = self.split_sample(batch_df)
672671

673672
if sample_df.shape[0] == 0:
@@ -890,7 +889,6 @@ def load_and_check_input(self) -> pd.DataFrame:
890889
loaded.shape
891890
# (1, 2)
892891
"""
893-
# TODO: Add unit tests for function
894892
# Load and check input
895893
input_df = pd.read_csv(self.args.input_file, sep=self.args.sep_input_file)
896894
logger.info("Number of rows in input file: %d" % input_df.shape[0])
@@ -932,7 +930,6 @@ def write_outputs(
932930
)
933931
cli.write_outputs(output_df, diagnostics_df)
934932
"""
935-
# TODO: Add unit tests for function
936933
# Write output
937934
output_df.to_csv(
938935
path_or_buf=self.args.output_file,

tests/test_cli.py

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,174 @@ def test_cli_weights_impact_on_outcome_method(self) -> None:
278278
cli_none = BalanceCLI(args_none)
279279
self.assertIsNone(cli_none.weights_impact_on_outcome_method())
280280

281+
def test_process_batch_returns_failure_payload_for_empty_sample(self) -> None:
282+
with tempfile.TemporaryDirectory() as temp_dir:
283+
input_file = os.path.join(temp_dir, "input.csv")
284+
output_file = os.path.join(temp_dir, "output.csv")
285+
parser = make_parser()
286+
args = parser.parse_args(
287+
[
288+
"--input_file",
289+
input_file,
290+
"--output_file",
291+
output_file,
292+
"--sample_column",
293+
"is_respondent",
294+
"--covariate_columns",
295+
"x",
296+
]
297+
)
298+
cli = BalanceCLI(args)
299+
300+
batch_df = pd.DataFrame(
301+
{
302+
"is_respondent": [0, 0],
303+
"id": [1, 2],
304+
"weight": [1.0, 1.0],
305+
"x": [1.0, 2.0],
306+
}
307+
)
308+
result = cli.process_batch(batch_df)
309+
310+
self.assertTrue(result["adjusted"].empty)
311+
self.assertEqual(
312+
result["diagnostics"].to_dict("records"),
313+
[
314+
{
315+
"metric": "adjustment_failure",
316+
"var": None,
317+
"val": 1,
318+
},
319+
{
320+
"metric": "adjustment_failure_reason",
321+
"var": None,
322+
"val": "No input data",
323+
},
324+
],
325+
)
326+
327+
def test_load_and_check_input_reads_file_and_columns(self) -> None:
328+
with tempfile.TemporaryDirectory() as temp_dir:
329+
input_file = os.path.join(temp_dir, "input.csv")
330+
output_file = os.path.join(temp_dir, "output.csv")
331+
parser = make_parser()
332+
args = parser.parse_args(
333+
[
334+
"--input_file",
335+
input_file,
336+
"--output_file",
337+
output_file,
338+
"--sample_column",
339+
"is_respondent",
340+
"--covariate_columns",
341+
"x",
342+
"--keep_row_column",
343+
"keep",
344+
]
345+
)
346+
cli = BalanceCLI(args)
347+
348+
input_df = pd.DataFrame(
349+
{
350+
"is_respondent": [1, 0],
351+
"id": [1, 2],
352+
"weight": [1.0, 1.0],
353+
"x": [1.0, 2.0],
354+
"keep": [1, 0],
355+
}
356+
)
357+
input_df.to_csv(input_file, index=False)
358+
359+
loaded = cli.load_and_check_input()
360+
pd.testing.assert_frame_equal(loaded, input_df)
361+
362+
def test_write_outputs_skips_diagnostics_when_no_output_path(self) -> None:
363+
with tempfile.TemporaryDirectory() as temp_dir:
364+
input_file = os.path.join(temp_dir, "input.csv")
365+
output_file = os.path.join(temp_dir, "output.csv")
366+
parser = make_parser()
367+
args = parser.parse_args(
368+
[
369+
"--input_file",
370+
input_file,
371+
"--output_file",
372+
output_file,
373+
"--sample_column",
374+
"is_respondent",
375+
"--covariate_columns",
376+
"x",
377+
]
378+
)
379+
cli = BalanceCLI(args)
380+
381+
output_df = pd.DataFrame({"id": [1], "weight": [1.25]})
382+
diagnostics_df = pd.DataFrame(
383+
{"metric": ["adjustment_failure"], "var": [None], "val": [0]}
384+
)
385+
386+
cli.write_outputs(output_df, diagnostics_df)
387+
388+
pd.testing.assert_frame_equal(
389+
pd.read_csv(output_file, sep=cli.args.sep_output_file),
390+
output_df,
391+
)
392+
self.assertIsNone(cli.args.diagnostics_output_file)
393+
394+
def test_write_outputs_writes_adjusted_and_diagnostics_with_custom_seps(
395+
self,
396+
) -> None:
397+
with tempfile.TemporaryDirectory() as temp_dir:
398+
input_file = os.path.join(temp_dir, "input.csv")
399+
output_file = os.path.join(temp_dir, "output.csv")
400+
diagnostics_output_file = os.path.join(temp_dir, "diagnostics.csv")
401+
parser = make_parser()
402+
args = parser.parse_args(
403+
[
404+
"--input_file",
405+
input_file,
406+
"--output_file",
407+
output_file,
408+
"--diagnostics_output_file",
409+
diagnostics_output_file,
410+
"--sample_column",
411+
"is_respondent",
412+
"--covariate_columns",
413+
"x",
414+
"--sep_output_file",
415+
"\t",
416+
"--sep_diagnostics_output_file",
417+
";",
418+
]
419+
)
420+
cli = BalanceCLI(args)
421+
422+
output_df = pd.DataFrame({"id": [1], "weight": [1.25]})
423+
diagnostics_df = pd.DataFrame(
424+
{
425+
"metric": ["adjustment_failure"],
426+
"var": [None],
427+
"val": [0],
428+
}
429+
)
430+
431+
cli.write_outputs(output_df, diagnostics_df)
432+
433+
pd.testing.assert_frame_equal(
434+
pd.read_csv(output_file, sep=cli.args.sep_output_file),
435+
output_df,
436+
)
437+
438+
diagnostics_loaded = pd.read_csv(
439+
diagnostics_output_file,
440+
sep=cli.args.sep_diagnostics_output_file,
441+
)
442+
self.assertEqual(
443+
diagnostics_loaded.loc[0, "metric"],
444+
"adjustment_failure",
445+
)
446+
self.assertTrue(pd.isna(diagnostics_loaded.loc[0, "var"]))
447+
self.assertEqual(diagnostics_loaded.loc[0, "val"], 0)
448+
281449
def test_cli_help(self) -> None:
282450
"""Test that CLI help command executes without errors."""
283451
parser = make_parser()

0 commit comments

Comments
 (0)