Skip to content

Commit 948e83e

Browse files
compare: fix mutually-exclusive false-positive on --baseline-tag + --comparison-branch (#535)
* compare: fix mutually-exclusive false-positive on --baseline-tag + --comparison-branch Why: - The baseline-tag conflict check in get_by_strings was reading comparison_covered, so any combination of --baseline-tag with --comparison-branch errored out as "--baseline-branch and --baseline-tag are mutually exclusive" — even though no baseline branch was provided. This blocked tag-vs-branch release verification comparisons that would otherwise be valid. - Separately, when defaults.yml sets baseline-branch, the fallback applied even when the user passed --baseline-tag, re-triggering the same spurious error. How: - get_by_strings: check baseline_covered (not comparison_covered) for the --baseline-tag conflict. - Extract resolve_baseline_branch() helper. When --baseline-tag is provided, skip the defaults-file branch fallback so the tag-only path stays valid through get_by_strings. - Add tests/test_compare_validation.py with 12 unit tests covering every valid combination, every invalid combination (still errors), and the defaults-fallback skip logic. * Bumping version from 0.12.29 to 0.12.30
1 parent 3a09393 commit 948e83e

3 files changed

Lines changed: 201 additions & 6 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "redisbench-admin"
3-
version = "0.12.29"
3+
version = "0.12.30"
44
description = "Redis benchmark run helper. A wrapper around Redis and Redis Modules benchmark tools ( ftsb_redisearch, memtier_benchmark, redis-benchmark, aibench, etc... )."
55
authors = ["filipecosta90 <filipecosta.90@gmail.com>","Redis Performance Group <performance@redis.com>"]
66
readme = "README.md"

redisbench_admin/compare/compare.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -380,14 +380,19 @@ def compare_command_logic(args, project_name, project_version):
380380
to_ts_ms = args.to_timestamp
381381
from_date = args.from_date
382382
to_date = args.to_date
383-
baseline_branch = args.baseline_branch
384-
if baseline_branch is None and default_baseline_branch is not None:
383+
baseline_branch = resolve_baseline_branch(
384+
args.baseline_branch, args.baseline_tag, default_baseline_branch
385+
)
386+
if (
387+
baseline_branch is not None
388+
and args.baseline_branch is None
389+
and args.baseline_tag is None
390+
):
385391
logging.info(
386392
"Given --baseline-branch was null using the default baseline branch {}".format(
387-
default_baseline_branch
393+
baseline_branch
388394
)
389395
)
390-
baseline_branch = default_baseline_branch
391396
comparison_branch = args.comparison_branch
392397
simplify_table = args.simple_table
393398
print_regressions_only = args.print_regressions_only
@@ -1239,6 +1244,23 @@ def get_percentage_value(row):
12391244
)
12401245

12411246

1247+
def resolve_baseline_branch(
1248+
arg_baseline_branch, arg_baseline_tag, default_baseline_branch
1249+
):
1250+
"""Resolve the effective baseline branch from CLI args + defaults-file fallback.
1251+
1252+
The defaults-file branch is only used when the user provided neither
1253+
--baseline-branch nor --baseline-tag. If --baseline-tag is set the
1254+
fallback is skipped, otherwise get_by_strings would raise a spurious
1255+
"mutually exclusive" error.
1256+
"""
1257+
if arg_baseline_branch is not None:
1258+
return arg_baseline_branch
1259+
if arg_baseline_tag is not None:
1260+
return None
1261+
return default_baseline_branch
1262+
1263+
12421264
def get_by_strings(
12431265
baseline_branch,
12441266
comparison_branch,
@@ -1261,7 +1283,7 @@ def get_by_strings(
12611283
comparison_str = comparison_branch
12621284

12631285
if baseline_tag is not None:
1264-
if comparison_covered:
1286+
if baseline_covered:
12651287
logging.error(
12661288
"--baseline-branch and --baseline-tag are mutually exclusive. Pick one..."
12671289
)

tests/test_compare_validation.py

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import logging
2+
3+
import pytest
4+
5+
from redisbench_admin.compare.compare import (
6+
get_by_strings,
7+
resolve_baseline_branch,
8+
)
9+
10+
11+
# ---------------------------------------------------------------------------
12+
# get_by_strings — valid combinations
13+
# ---------------------------------------------------------------------------
14+
15+
16+
def test_get_by_strings_branch_vs_branch():
17+
baseline_str, by_baseline, comparison_str, by_comparison = get_by_strings(
18+
baseline_branch="master",
19+
comparison_branch="feature",
20+
baseline_tag=None,
21+
comparison_tag=None,
22+
)
23+
assert (baseline_str, by_baseline) == ("master", "branch")
24+
assert (comparison_str, by_comparison) == ("feature", "branch")
25+
26+
27+
def test_get_by_strings_tag_baseline_branch_comparison():
28+
"""Regression: this combo previously errored as mutually exclusive
29+
because the baseline-tag check was reading comparison_covered."""
30+
baseline_str, by_baseline, comparison_str, by_comparison = get_by_strings(
31+
baseline_branch=None,
32+
comparison_branch="feature",
33+
baseline_tag="v1.0",
34+
comparison_tag=None,
35+
)
36+
assert (baseline_str, by_baseline) == ("v1.0", "version")
37+
assert (comparison_str, by_comparison) == ("feature", "branch")
38+
39+
40+
def test_get_by_strings_branch_baseline_tag_comparison():
41+
baseline_str, by_baseline, comparison_str, by_comparison = get_by_strings(
42+
baseline_branch="master",
43+
comparison_branch=None,
44+
baseline_tag=None,
45+
comparison_tag="v2.0",
46+
)
47+
assert (baseline_str, by_baseline) == ("master", "branch")
48+
assert (comparison_str, by_comparison) == ("v2.0", "version")
49+
50+
51+
def test_get_by_strings_tag_vs_tag():
52+
baseline_str, by_baseline, comparison_str, by_comparison = get_by_strings(
53+
baseline_branch=None,
54+
comparison_branch=None,
55+
baseline_tag="v1.0",
56+
comparison_tag="v2.0",
57+
)
58+
assert (baseline_str, by_baseline) == ("v1.0", "version")
59+
assert (comparison_str, by_comparison) == ("v2.0", "version")
60+
61+
62+
# ---------------------------------------------------------------------------
63+
# get_by_strings — invalid combinations should still raise SystemExit
64+
# ---------------------------------------------------------------------------
65+
66+
67+
def test_get_by_strings_baseline_branch_and_tag_rejected(caplog):
68+
with caplog.at_level(logging.ERROR), pytest.raises(SystemExit) as excinfo:
69+
get_by_strings(
70+
baseline_branch="master",
71+
comparison_branch="feature",
72+
baseline_tag="v1.0",
73+
comparison_tag=None,
74+
)
75+
assert excinfo.value.code == 1
76+
assert any(
77+
"baseline-branch and --baseline-tag are mutually exclusive" in r.message
78+
for r in caplog.records
79+
)
80+
81+
82+
def test_get_by_strings_comparison_branch_and_tag_rejected(caplog):
83+
with caplog.at_level(logging.ERROR), pytest.raises(SystemExit) as excinfo:
84+
get_by_strings(
85+
baseline_branch="master",
86+
comparison_branch="feature",
87+
baseline_tag=None,
88+
comparison_tag="v2.0",
89+
)
90+
assert excinfo.value.code == 1
91+
assert any(
92+
"comparison-branch and --comparison-tag are mutually exclusive" in r.message
93+
for r in caplog.records
94+
)
95+
96+
97+
def test_get_by_strings_no_baseline_rejected(caplog):
98+
with caplog.at_level(logging.ERROR), pytest.raises(SystemExit) as excinfo:
99+
get_by_strings(
100+
baseline_branch=None,
101+
comparison_branch="feature",
102+
baseline_tag=None,
103+
comparison_tag=None,
104+
)
105+
assert excinfo.value.code == 1
106+
assert any(
107+
"--baseline-branch or --baseline-tag" in r.message for r in caplog.records
108+
)
109+
110+
111+
def test_get_by_strings_no_comparison_rejected(caplog):
112+
with caplog.at_level(logging.ERROR), pytest.raises(SystemExit) as excinfo:
113+
get_by_strings(
114+
baseline_branch="master",
115+
comparison_branch=None,
116+
baseline_tag=None,
117+
comparison_tag=None,
118+
)
119+
assert excinfo.value.code == 1
120+
assert any(
121+
"--comparison-branch or --comparison-tag" in r.message for r in caplog.records
122+
)
123+
124+
125+
# ---------------------------------------------------------------------------
126+
# resolve_baseline_branch — defaults-file fallback
127+
# ---------------------------------------------------------------------------
128+
129+
130+
def test_resolve_baseline_branch_explicit_branch_wins():
131+
assert (
132+
resolve_baseline_branch(
133+
arg_baseline_branch="explicit",
134+
arg_baseline_tag=None,
135+
default_baseline_branch="master",
136+
)
137+
== "explicit"
138+
)
139+
140+
141+
def test_resolve_baseline_branch_falls_back_to_default():
142+
assert (
143+
resolve_baseline_branch(
144+
arg_baseline_branch=None,
145+
arg_baseline_tag=None,
146+
default_baseline_branch="master",
147+
)
148+
== "master"
149+
)
150+
151+
152+
def test_resolve_baseline_branch_skips_fallback_when_tag_set():
153+
"""Regression: if --baseline-tag is given, the defaults-file branch must
154+
not be auto-applied — otherwise get_by_strings raises mutually exclusive."""
155+
assert (
156+
resolve_baseline_branch(
157+
arg_baseline_branch=None,
158+
arg_baseline_tag="v1.0",
159+
default_baseline_branch="master",
160+
)
161+
is None
162+
)
163+
164+
165+
def test_resolve_baseline_branch_no_default_no_fallback():
166+
assert (
167+
resolve_baseline_branch(
168+
arg_baseline_branch=None,
169+
arg_baseline_tag=None,
170+
default_baseline_branch=None,
171+
)
172+
is None
173+
)

0 commit comments

Comments
 (0)