openretailscience 0.45.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openretailscience-0.45.0/.claude/commands/review-pr-tests.md +607 -0
- openretailscience-0.45.0/.env_sample +9 -0
- openretailscience-0.45.0/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
- openretailscience-0.45.0/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- openretailscience-0.45.0/.github/dependabot.yml +15 -0
- openretailscience-0.45.0/.github/workflows/bigquery-integration.yml +67 -0
- openretailscience-0.45.0/.github/workflows/claude-code-review.yml +55 -0
- openretailscience-0.45.0/.github/workflows/claude.yml +112 -0
- openretailscience-0.45.0/.github/workflows/cloudflare-docs-preview.yml +99 -0
- openretailscience-0.45.0/.github/workflows/gh-page.yml +45 -0
- openretailscience-0.45.0/.github/workflows/pyspark-integration.yml +63 -0
- openretailscience-0.45.0/.github/workflows/release.yml +220 -0
- openretailscience-0.45.0/.github/workflows/slack-notifications.yml +103 -0
- openretailscience-0.45.0/.github/workflows/snowflake-integration.yml +86 -0
- openretailscience-0.45.0/.github/workflows/test.yml +75 -0
- openretailscience-0.45.0/.gitignore +144 -0
- openretailscience-0.45.0/.markdownlint.json +10 -0
- openretailscience-0.45.0/.pre-commit-config.yaml +56 -0
- openretailscience-0.45.0/CLAUDE.md +154 -0
- openretailscience-0.45.0/CNAME +1 -0
- openretailscience-0.45.0/CODE_OF_CONDUCT.md +127 -0
- openretailscience-0.45.0/CONTRIBUTING.md +51 -0
- openretailscience-0.45.0/LICENSE +93 -0
- openretailscience-0.45.0/PKG-INFO +364 -0
- openretailscience-0.45.0/README.md +335 -0
- openretailscience-0.45.0/data/transactions.parquet +0 -0
- openretailscience-0.45.0/docs/analysis_modules.md +1688 -0
- openretailscience-0.45.0/docs/api/analysis/cohort.md +3 -0
- openretailscience-0.45.0/docs/api/analysis/composite_rank.md +3 -0
- openretailscience-0.45.0/docs/api/analysis/cross_shop.md +3 -0
- openretailscience-0.45.0/docs/api/analysis/customer.md +3 -0
- openretailscience-0.45.0/docs/api/analysis/customer_decision_hierarchy.md +3 -0
- openretailscience-0.45.0/docs/api/analysis/gain_loss.md +3 -0
- openretailscience-0.45.0/docs/api/analysis/haversine.md +3 -0
- openretailscience-0.45.0/docs/api/analysis/product_association.md +3 -0
- openretailscience-0.45.0/docs/api/analysis/revenue_tree.md +3 -0
- openretailscience-0.45.0/docs/api/metrics/distribution.md +5 -0
- openretailscience-0.45.0/docs/api/options.md +3 -0
- openretailscience-0.45.0/docs/api/plots/area.md +3 -0
- openretailscience-0.45.0/docs/api/plots/bar.md +3 -0
- openretailscience-0.45.0/docs/api/plots/broken_timeline.md +3 -0
- openretailscience-0.45.0/docs/api/plots/cohort.md +3 -0
- openretailscience-0.45.0/docs/api/plots/heatmap.md +3 -0
- openretailscience-0.45.0/docs/api/plots/histogram.md +3 -0
- openretailscience-0.45.0/docs/api/plots/index.md +3 -0
- openretailscience-0.45.0/docs/api/plots/line.md +3 -0
- openretailscience-0.45.0/docs/api/plots/period_on_period.md +3 -0
- openretailscience-0.45.0/docs/api/plots/price.md +3 -0
- openretailscience-0.45.0/docs/api/plots/scatter.md +3 -0
- openretailscience-0.45.0/docs/api/plots/styles/graph_utils.md +3 -0
- openretailscience-0.45.0/docs/api/plots/styles/styling_helpers.md +3 -0
- openretailscience-0.45.0/docs/api/plots/time.md +3 -0
- openretailscience-0.45.0/docs/api/plots/venn.md +3 -0
- openretailscience-0.45.0/docs/api/plots/waterfall.md +3 -0
- openretailscience-0.45.0/docs/api/segmentation/hml.md +3 -0
- openretailscience-0.45.0/docs/api/segmentation/nlr.md +3 -0
- openretailscience-0.45.0/docs/api/segmentation/rfm.md +3 -0
- openretailscience-0.45.0/docs/api/segmentation/segstats.md +3 -0
- openretailscience-0.45.0/docs/api/segmentation/threshold.md +3 -0
- openretailscience-0.45.0/docs/api/utils/date.md +3 -0
- openretailscience-0.45.0/docs/api/utils/filter_and_label.md +3 -0
- openretailscience-0.45.0/docs/api/utils/label.md +3 -0
- openretailscience-0.45.0/docs/assets/gallery/area_thumbnail.png +0 -0
- openretailscience-0.45.0/docs/assets/gallery/bar_thumbnail.png +0 -0
- openretailscience-0.45.0/docs/assets/gallery/broken_timeline_thumbnail.png +0 -0
- openretailscience-0.45.0/docs/assets/gallery/cohort_thumbnail.png +0 -0
- openretailscience-0.45.0/docs/assets/gallery/heatmap_thumbnail.png +0 -0
- openretailscience-0.45.0/docs/assets/gallery/histogram_thumbnail.png +0 -0
- openretailscience-0.45.0/docs/assets/gallery/index_thumbnail.png +0 -0
- openretailscience-0.45.0/docs/assets/gallery/line_thumbnail.png +0 -0
- openretailscience-0.45.0/docs/assets/gallery/period_on_period_thumbnail.png +0 -0
- openretailscience-0.45.0/docs/assets/gallery/price_thumbnail.png +0 -0
- openretailscience-0.45.0/docs/assets/gallery/scatter_thumbnail.png +0 -0
- openretailscience-0.45.0/docs/assets/gallery/time_thumbnail.png +0 -0
- openretailscience-0.45.0/docs/assets/gallery/venn_thumbnail.png +0 -0
- openretailscience-0.45.0/docs/assets/gallery/waterfall_thumbnail.png +0 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/cross_shop.svg +1997 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/customer_decision_hierarchy.svg +2795 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/days_between_purchases.svg +2431 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/gain_loss.svg +2160 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/hml_segmentation.svg +1629 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/plots/area.svg +2145 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/plots/bar_plot.svg +2443 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/plots/broken_timeline.svg +2015 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/plots/cohort.svg +4454 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/plots/heatmap.svg +3531 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/plots/histogram_plot.svg +2453 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/plots/index_plot.svg +2263 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/plots/line_plot.svg +2025 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/plots/period_on_period.svg +2177 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/plots/price_plot.svg +2746 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/plots/scatter.svg +2373 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/plots/venn.svg +2177 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/purchases_per_customer.svg +2065 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/revenue_tree.svg +2712 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/threshold_segmentation.svg +1750 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/time_plot.svg +1 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/transaction_churn.svg +2074 -0
- openretailscience-0.45.0/docs/assets/images/analysis_modules/waterfall.svg +1854 -0
- openretailscience-0.45.0/docs/assets/images/favicon.svg +66 -0
- openretailscience-0.45.0/docs/assets/images/features/charts.png +0 -0
- openretailscience-0.45.0/docs/assets/images/features/cross_shop.png +0 -0
- openretailscience-0.45.0/docs/assets/images/logo.svg +22 -0
- openretailscience-0.45.0/docs/examples/cross_shop.ipynb +988 -0
- openretailscience-0.45.0/docs/examples/gain_loss.ipynb +887 -0
- openretailscience-0.45.0/docs/examples/product_association.ipynb +685 -0
- openretailscience-0.45.0/docs/examples/retention.ipynb +456 -0
- openretailscience-0.45.0/docs/examples/revenue_tree.ipynb +307 -0
- openretailscience-0.45.0/docs/examples/segmentation.ipynb +854 -0
- openretailscience-0.45.0/docs/gallery/index.md +199 -0
- openretailscience-0.45.0/docs/gallery/plots/area.ipynb +412 -0
- openretailscience-0.45.0/docs/gallery/plots/bar.ipynb +568 -0
- openretailscience-0.45.0/docs/gallery/plots/broken_timeline.ipynb +284 -0
- openretailscience-0.45.0/docs/gallery/plots/cohort.ipynb +161 -0
- openretailscience-0.45.0/docs/gallery/plots/heatmap.ipynb +155 -0
- openretailscience-0.45.0/docs/gallery/plots/histogram.ipynb +209 -0
- openretailscience-0.45.0/docs/gallery/plots/index_plot.ipynb +701 -0
- openretailscience-0.45.0/docs/gallery/plots/line.ipynb +428 -0
- openretailscience-0.45.0/docs/gallery/plots/period_on_period.ipynb +153 -0
- openretailscience-0.45.0/docs/gallery/plots/price.ipynb +176 -0
- openretailscience-0.45.0/docs/gallery/plots/scatter.ipynb +199 -0
- openretailscience-0.45.0/docs/gallery/plots/time.ipynb +247 -0
- openretailscience-0.45.0/docs/gallery/plots/venn.ipynb +239 -0
- openretailscience-0.45.0/docs/gallery/plots/waterfall.ipynb +266 -0
- openretailscience-0.45.0/docs/getting_started/installation.md +16 -0
- openretailscience-0.45.0/docs/getting_started/options_guide.md +416 -0
- openretailscience-0.45.0/docs/index.md +9 -0
- openretailscience-0.45.0/docs/javascripts/mathjax.js +19 -0
- openretailscience-0.45.0/docs/metrics.md +85 -0
- openretailscience-0.45.0/docs/overrides/home.html +232 -0
- openretailscience-0.45.0/docs/overrides/main.html +7 -0
- openretailscience-0.45.0/docs/robots.txt +5 -0
- openretailscience-0.45.0/docs/stylesheets/extra.css +915 -0
- openretailscience-0.45.0/mkdocs.yml +151 -0
- openretailscience-0.45.0/openretailscience/__init__.py +3 -0
- openretailscience-0.45.0/openretailscience/analysis/__init__.py +0 -0
- openretailscience-0.45.0/openretailscience/analysis/cohort.py +185 -0
- openretailscience-0.45.0/openretailscience/analysis/composite_rank.py +372 -0
- openretailscience-0.45.0/openretailscience/analysis/cross_shop.py +354 -0
- openretailscience-0.45.0/openretailscience/analysis/customer.py +459 -0
- openretailscience-0.45.0/openretailscience/analysis/customer_decision_hierarchy.py +359 -0
- openretailscience-0.45.0/openretailscience/analysis/gain_loss.py +340 -0
- openretailscience-0.45.0/openretailscience/analysis/haversine.py +50 -0
- openretailscience-0.45.0/openretailscience/analysis/product_association.py +418 -0
- openretailscience-0.45.0/openretailscience/analysis/revenue_tree.py +553 -0
- openretailscience-0.45.0/openretailscience/assets/fonts/Poppins-Bold.ttf +0 -0
- openretailscience-0.45.0/openretailscience/assets/fonts/Poppins-LightItalic.ttf +0 -0
- openretailscience-0.45.0/openretailscience/assets/fonts/Poppins-Medium.ttf +0 -0
- openretailscience-0.45.0/openretailscience/assets/fonts/Poppins-Regular.ttf +0 -0
- openretailscience-0.45.0/openretailscience/assets/fonts/Poppins-SemiBold.ttf +0 -0
- openretailscience-0.45.0/openretailscience/constants.py +295 -0
- openretailscience-0.45.0/openretailscience/metrics/__init__.py +0 -0
- openretailscience-0.45.0/openretailscience/metrics/base.py +29 -0
- openretailscience-0.45.0/openretailscience/metrics/distribution/__init__.py +0 -0
- openretailscience-0.45.0/openretailscience/metrics/distribution/acv.py +81 -0
- openretailscience-0.45.0/openretailscience/metrics/distribution/pct_of_stores.py +118 -0
- openretailscience-0.45.0/openretailscience/options.py +842 -0
- openretailscience-0.45.0/openretailscience/plots/__init__.py +0 -0
- openretailscience-0.45.0/openretailscience/plots/area.py +114 -0
- openretailscience-0.45.0/openretailscience/plots/bar.py +375 -0
- openretailscience-0.45.0/openretailscience/plots/broken_timeline.py +197 -0
- openretailscience-0.45.0/openretailscience/plots/cohort.py +94 -0
- openretailscience-0.45.0/openretailscience/plots/heatmap.py +134 -0
- openretailscience-0.45.0/openretailscience/plots/histogram.py +276 -0
- openretailscience-0.45.0/openretailscience/plots/index.py +505 -0
- openretailscience-0.45.0/openretailscience/plots/line.py +379 -0
- openretailscience-0.45.0/openretailscience/plots/period_on_period.py +144 -0
- openretailscience-0.45.0/openretailscience/plots/price.py +275 -0
- openretailscience-0.45.0/openretailscience/plots/scatter.py +382 -0
- openretailscience-0.45.0/openretailscience/plots/styles/__init__.py +1 -0
- openretailscience-0.45.0/openretailscience/plots/styles/colors.py +215 -0
- openretailscience-0.45.0/openretailscience/plots/styles/font_utils.py +62 -0
- openretailscience-0.45.0/openretailscience/plots/styles/graph_utils.py +834 -0
- openretailscience-0.45.0/openretailscience/plots/styles/styling_helpers.py +155 -0
- openretailscience-0.45.0/openretailscience/plots/time.py +160 -0
- openretailscience-0.45.0/openretailscience/plots/tree_diagram.py +813 -0
- openretailscience-0.45.0/openretailscience/plots/venn.py +127 -0
- openretailscience-0.45.0/openretailscience/plots/waterfall.py +217 -0
- openretailscience-0.45.0/openretailscience/plugin.py +238 -0
- openretailscience-0.45.0/openretailscience/segmentation/__init__.py +0 -0
- openretailscience-0.45.0/openretailscience/segmentation/hml.py +97 -0
- openretailscience-0.45.0/openretailscience/segmentation/nlr.py +218 -0
- openretailscience-0.45.0/openretailscience/segmentation/rfm.py +307 -0
- openretailscience-0.45.0/openretailscience/segmentation/segstats.py +1177 -0
- openretailscience-0.45.0/openretailscience/segmentation/threshold.py +165 -0
- openretailscience-0.45.0/openretailscience/utils/__init__.py +0 -0
- openretailscience-0.45.0/openretailscience/utils/date.py +226 -0
- openretailscience-0.45.0/openretailscience/utils/filter_and_label.py +52 -0
- openretailscience-0.45.0/openretailscience/utils/label.py +79 -0
- openretailscience-0.45.0/openretailscience/utils/validation.py +45 -0
- openretailscience-0.45.0/options_template.toml +77 -0
- openretailscience-0.45.0/pyproject.toml +166 -0
- openretailscience-0.45.0/readme_assets/cross_shop_chart.png +0 -0
- openretailscience-0.45.0/readme_assets/days_until_next_purchase.png +0 -0
- openretailscience-0.45.0/readme_assets/gain_loss_chart.png +0 -0
- openretailscience-0.45.0/readme_assets/logo.png +0 -0
- openretailscience-0.45.0/readme_assets/new_store_cannibalization.png +0 -0
- openretailscience-0.45.0/taplo.toml +33 -0
- openretailscience-0.45.0/tests/analysis/test_cohort.py +185 -0
- openretailscience-0.45.0/tests/analysis/test_composite_rank.py +584 -0
- openretailscience-0.45.0/tests/analysis/test_cross_shop.py +726 -0
- openretailscience-0.45.0/tests/analysis/test_customer.py +98 -0
- openretailscience-0.45.0/tests/analysis/test_customer_decision_hierarchy.py +153 -0
- openretailscience-0.45.0/tests/analysis/test_gain_loss.py +285 -0
- openretailscience-0.45.0/tests/analysis/test_haversine.py +35 -0
- openretailscience-0.45.0/tests/analysis/test_product_association.py +371 -0
- openretailscience-0.45.0/tests/analysis/test_revenue_tree.py +769 -0
- openretailscience-0.45.0/tests/integration/conftest.py +47 -0
- openretailscience-0.45.0/tests/integration/test_cohort_analysis.py +29 -0
- openretailscience-0.45.0/tests/integration/test_composite_rank.py +32 -0
- openretailscience-0.45.0/tests/integration/test_cross_shop.py +58 -0
- openretailscience-0.45.0/tests/integration/test_customer_decision_hierarchy.py +42 -0
- openretailscience-0.45.0/tests/integration/test_date.py +29 -0
- openretailscience-0.45.0/tests/integration/test_filter_and_label.py +29 -0
- openretailscience-0.45.0/tests/integration/test_haversine.py +26 -0
- openretailscience-0.45.0/tests/integration/test_hml_segmentation.py +38 -0
- openretailscience-0.45.0/tests/integration/test_product_association.py +41 -0
- openretailscience-0.45.0/tests/integration/test_revenue_tree.py +62 -0
- openretailscience-0.45.0/tests/integration/test_rfm_segmentation.py +72 -0
- openretailscience-0.45.0/tests/integration/test_segstats_segmentation.py +75 -0
- openretailscience-0.45.0/tests/integration/test_threshold_segmentation.py +43 -0
- openretailscience-0.45.0/tests/metrics/distribution/test_acv.py +121 -0
- openretailscience-0.45.0/tests/metrics/distribution/test_pct_of_stores.py +267 -0
- openretailscience-0.45.0/tests/metrics/test_base.py +63 -0
- openretailscience-0.45.0/tests/plots/styles/test_color.py +127 -0
- openretailscience-0.45.0/tests/plots/styles/test_font_utils.py +39 -0
- openretailscience-0.45.0/tests/plots/styles/test_graph_utils.py +934 -0
- openretailscience-0.45.0/tests/plots/styles/test_styling_helper.py +114 -0
- openretailscience-0.45.0/tests/plots/test_area.py +153 -0
- openretailscience-0.45.0/tests/plots/test_bar.py +528 -0
- openretailscience-0.45.0/tests/plots/test_broken_timeline.py +351 -0
- openretailscience-0.45.0/tests/plots/test_cohort.py +163 -0
- openretailscience-0.45.0/tests/plots/test_heatmap.py +246 -0
- openretailscience-0.45.0/tests/plots/test_histogram.py +343 -0
- openretailscience-0.45.0/tests/plots/test_index.py +983 -0
- openretailscience-0.45.0/tests/plots/test_line.py +649 -0
- openretailscience-0.45.0/tests/plots/test_period_on_period.py +144 -0
- openretailscience-0.45.0/tests/plots/test_price.py +548 -0
- openretailscience-0.45.0/tests/plots/test_scatter.py +762 -0
- openretailscience-0.45.0/tests/plots/test_styling_options_integration.py +216 -0
- openretailscience-0.45.0/tests/plots/test_time.py +94 -0
- openretailscience-0.45.0/tests/plots/test_tree_diagram.py +827 -0
- openretailscience-0.45.0/tests/plots/test_venn.py +100 -0
- openretailscience-0.45.0/tests/plots/test_waterfall.py +186 -0
- openretailscience-0.45.0/tests/segmentation/test_hml.py +222 -0
- openretailscience-0.45.0/tests/segmentation/test_nlr.py +393 -0
- openretailscience-0.45.0/tests/segmentation/test_rfm.py +482 -0
- openretailscience-0.45.0/tests/segmentation/test_segstats.py +2108 -0
- openretailscience-0.45.0/tests/segmentation/test_threshold.py +293 -0
- openretailscience-0.45.0/tests/test_options.py +267 -0
- openretailscience-0.45.0/tests/test_plugin.py +367 -0
- openretailscience-0.45.0/tests/toml_files/corrupt.toml +1 -0
- openretailscience-0.45.0/tests/toml_files/invalid_option.toml +3 -0
- openretailscience-0.45.0/tests/toml_files/test_options.toml +42 -0
- openretailscience-0.45.0/tests/toml_files/valid.toml +12 -0
- openretailscience-0.45.0/tests/utils/test_date.py +393 -0
- openretailscience-0.45.0/tests/utils/test_filter_and_label.py +71 -0
- openretailscience-0.45.0/tests/utils/test_label.py +173 -0
- openretailscience-0.45.0/tests/utils/test_validation.py +87 -0
- openretailscience-0.45.0/tox.ini +17 -0
- openretailscience-0.45.0/uv.lock +3757 -0
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
# Input Validation
|
|
2
|
+
if [ -z "$1" ]; then
|
|
3
|
+
echo "Error: PR number is required. Usage: /review-pr-tests <PR_NUMBER>"
|
|
4
|
+
exit 1
|
|
5
|
+
fi
|
|
6
|
+
|
|
7
|
+
You are reviewing all Python tests in PR #$1 for the OpenRetailScience package.
|
|
8
|
+
|
|
9
|
+
## Initial Analysis Steps:
|
|
10
|
+
1. **First, check the PR using `gh pr view $1` and `gh pr diff $1`** to see the changes
|
|
11
|
+
2. **If you need to examine test files locally**, check out the PR:
|
|
12
|
+
```bash
|
|
13
|
+
# This automatically fetches and checks out the PR's branch
|
|
14
|
+
# (handles both local and remote branches)
|
|
15
|
+
gh pr checkout $1
|
|
16
|
+
```
|
|
17
|
+
3. **Check if there are any Python test files** in the PR (files matching `test_*.py` or `*_test.py`). If no test files exist, provide a brief note about missing tests
|
|
18
|
+
4. **Identify which OpenRetailScience module is being tested** by analyzing the imports and function calls in the code
|
|
19
|
+
|
|
20
|
+
If there are no test files in the PR, respond with:
|
|
21
|
+
```
|
|
22
|
+
## NO TEST FILES FOUND IN PR #$1
|
|
23
|
+
|
|
24
|
+
This PR does not contain any test files. Consider adding tests for:
|
|
25
|
+
- [List key functionality that should be tested based on the PR changes]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Otherwise, identify tests that fall into the following problematic categories:
|
|
29
|
+
|
|
30
|
+
## 1. Tests That Don't Test Package Code
|
|
31
|
+
Tests that make assertions but never call any functions, classes, or modules from the package under test. Look for tests that only test local variables, Python built-ins, or standard library functions without involving the actual package.
|
|
32
|
+
|
|
33
|
+
**Red Flags:**
|
|
34
|
+
- No imports from the package being tested
|
|
35
|
+
- Only tests variable assignments or basic Python operations
|
|
36
|
+
- Never calls functions/methods from the package
|
|
37
|
+
|
|
38
|
+
**Example:**
|
|
39
|
+
```python
|
|
40
|
+
def test_basic_math():
|
|
41
|
+
# BAD: Never uses the package under test
|
|
42
|
+
x = 1
|
|
43
|
+
y = 2
|
|
44
|
+
assert x + y == 3
|
|
45
|
+
|
|
46
|
+
def test_string_operations():
|
|
47
|
+
# BAD: Only tests Python built-in string methods
|
|
48
|
+
name = "John"
|
|
49
|
+
assert name.upper() == "JOHN"
|
|
50
|
+
assert len(name) == 4
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 2. Tests That Primarily Test Library Functionality
|
|
54
|
+
Tests that focus on verifying external library behavior rather than how the package under test uses those libraries. These tests essentially re-test third-party libraries that should already have their own test suites.
|
|
55
|
+
|
|
56
|
+
**Red Flags:**
|
|
57
|
+
- Directly calls library functions without package wrapper
|
|
58
|
+
- Tests properties of library outputs (e.g., UUID format, datetime formatting)
|
|
59
|
+
- Could be moved to the library's own test suite without loss
|
|
60
|
+
|
|
61
|
+
**Example:**
|
|
62
|
+
```python
|
|
63
|
+
import uuid
|
|
64
|
+
import pandas as pd
|
|
65
|
+
from datetime import datetime
|
|
66
|
+
|
|
67
|
+
def test_uuid_generation():
|
|
68
|
+
# BAD: Tests uuid library, not the package's use of it
|
|
69
|
+
generated_id = str(uuid.uuid4())
|
|
70
|
+
assert len(generated_id) == 36
|
|
71
|
+
assert generated_id.count("-") == 4
|
|
72
|
+
|
|
73
|
+
def test_pandas_dataframe():
|
|
74
|
+
# BAD: Tests pandas functionality, not package code
|
|
75
|
+
df = pd.DataFrame({'A': [1, 2], 'B': [3, 4]})
|
|
76
|
+
assert len(df) == 2
|
|
77
|
+
assert 'A' in df.columns
|
|
78
|
+
|
|
79
|
+
# GOOD: Tests package's use of the library
|
|
80
|
+
def test_crossshop_analysis():
|
|
81
|
+
from openretailscience.crossshop import CrossShop
|
|
82
|
+
analyzer = CrossShop(store_col="store_id")
|
|
83
|
+
result = analyzer.fit(df)
|
|
84
|
+
assert result.shape[0] > 0 # Tests that package processes data
|
|
85
|
+
assert "customer_id" in result.columns # Tests package output
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 3. Tests With Substantial Duplication
|
|
89
|
+
Tests that repeat significant portions of logic, assertions, or setup from other tests without adding meaningful coverage. Look for copy-pasted test code with minor variations that don't test different behavior.
|
|
90
|
+
|
|
91
|
+
**Red Flags:**
|
|
92
|
+
- Multiple tests with nearly identical setup code
|
|
93
|
+
- Same assertions repeated across tests with minor value changes
|
|
94
|
+
- Tests that differ only in input values but test the same code path
|
|
95
|
+
- Could be combined using parametrize or merged into one test
|
|
96
|
+
|
|
97
|
+
**Example:**
|
|
98
|
+
```python
|
|
99
|
+
def test_add_positive_numbers():
|
|
100
|
+
calc = Calculator()
|
|
101
|
+
result = calc.add(5, 3)
|
|
102
|
+
assert result == 8
|
|
103
|
+
assert result > 0
|
|
104
|
+
assert isinstance(result, int)
|
|
105
|
+
|
|
106
|
+
def test_add_different_positive_numbers():
|
|
107
|
+
# BAD: Duplicates above test with different values
|
|
108
|
+
calc = Calculator()
|
|
109
|
+
result = calc.add(10, 7)
|
|
110
|
+
assert result == 17
|
|
111
|
+
assert result > 0
|
|
112
|
+
assert isinstance(result, int)
|
|
113
|
+
|
|
114
|
+
def test_add_more_numbers():
|
|
115
|
+
# BAD: Same pattern again
|
|
116
|
+
calc = Calculator()
|
|
117
|
+
result = calc.add(2, 4)
|
|
118
|
+
assert result == 6
|
|
119
|
+
assert result > 0
|
|
120
|
+
assert isinstance(result, int)
|
|
121
|
+
|
|
122
|
+
# BETTER: Use parametrize or combine into one comprehensive test
|
|
123
|
+
@pytest.mark.parametrize("a,b,expected", [
|
|
124
|
+
(5, 3, 8),
|
|
125
|
+
(10, 7, 17),
|
|
126
|
+
(2, 4, 6)
|
|
127
|
+
])
|
|
128
|
+
def test_add_positive_numbers(a, b, expected):
|
|
129
|
+
calc = Calculator()
|
|
130
|
+
result = calc.add(a, b)
|
|
131
|
+
assert result == expected
|
|
132
|
+
assert result > 0
|
|
133
|
+
assert isinstance(result, int)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## 4. Tests That Cover Only Basic Language/Library Features
|
|
137
|
+
Tests that verify fundamental Python or standard library behavior without meaningful connection to OpenRetailScience's functionality. These tests essentially verify that Python works as expected.
|
|
138
|
+
|
|
139
|
+
**Red Flags:**
|
|
140
|
+
- Tests basic Python operations (list, dict, set operations)
|
|
141
|
+
- Verifies standard library behavior that's guaranteed by Python
|
|
142
|
+
- No connection to business logic or package functionality
|
|
143
|
+
- Could be in Python's own test suite
|
|
144
|
+
|
|
145
|
+
**Example:**
|
|
146
|
+
```python
|
|
147
|
+
def test_basic_set_operations():
|
|
148
|
+
# BAD: Tests Python's set behavior
|
|
149
|
+
assert len(set(["Hello", "World"])) == 2
|
|
150
|
+
assert "Hello" in set(["Hello", "World"])
|
|
151
|
+
|
|
152
|
+
def test_dictionary_basics():
|
|
153
|
+
# BAD: Tests Python dict functionality
|
|
154
|
+
d = {"key": "value"}
|
|
155
|
+
assert d.get("key") == "value"
|
|
156
|
+
assert d.get("missing", "default") == "default"
|
|
157
|
+
|
|
158
|
+
def test_list_operations():
|
|
159
|
+
# BAD: Tests Python list methods
|
|
160
|
+
items = [1, 2, 3]
|
|
161
|
+
items.append(4)
|
|
162
|
+
assert len(items) == 4
|
|
163
|
+
assert items[-1] == 4
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Additional Problematic Patterns to Identify:
|
|
167
|
+
|
|
168
|
+
### 5. Trivial or Tautological Tests
|
|
169
|
+
Tests that assert obvious truths, constants equal themselves, or test definitions rather than behavior. These provide no value and false confidence in coverage metrics.
|
|
170
|
+
|
|
171
|
+
**Red Flags:**
|
|
172
|
+
- Tests that a constant equals its defined value
|
|
173
|
+
- Assertions that are always true by definition
|
|
174
|
+
- Tests that verify class/function names or attributes exist
|
|
175
|
+
- No actual behavior or logic being tested
|
|
176
|
+
|
|
177
|
+
**Example:**
|
|
178
|
+
```python
|
|
179
|
+
# Assuming MAX_STORES = 100 in openretailscience.config
|
|
180
|
+
def test_constant():
|
|
181
|
+
from openretailscience.config import MAX_STORES
|
|
182
|
+
# BAD: Just tests that 100 == 100
|
|
183
|
+
assert MAX_STORES == 100
|
|
184
|
+
|
|
185
|
+
def test_true_is_true():
|
|
186
|
+
# BAD: Tautological
|
|
187
|
+
assert True == True
|
|
188
|
+
assert 1 == 1
|
|
189
|
+
|
|
190
|
+
def test_class_name():
|
|
191
|
+
from openretailscience.style import BaseStyle
|
|
192
|
+
# BAD: Tests Python's class system, not package behavior
|
|
193
|
+
obj = BaseStyle()
|
|
194
|
+
assert obj.__class__.__name__ == "BaseStyle"
|
|
195
|
+
|
|
196
|
+
def test_config_values():
|
|
197
|
+
# BAD if CONFIG is just {"debug": False}
|
|
198
|
+
from openretailscience import CONFIG
|
|
199
|
+
assert CONFIG["debug"] == False # Just repeating the definition
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### 6. Tests With No Meaningful Assertions
|
|
203
|
+
Tests that either have no assertions, only assert True, or have assertions that can never fail. These tests may execute code but don't verify behavior or outcomes.
|
|
204
|
+
|
|
205
|
+
**Red Flags:**
|
|
206
|
+
- No assert statements at all
|
|
207
|
+
- Only checks if attributes/methods exist
|
|
208
|
+
- Assertions that can never fail given the test setup
|
|
209
|
+
- Tests that only verify successful execution without checking results
|
|
210
|
+
|
|
211
|
+
**Example:**
|
|
212
|
+
```python
|
|
213
|
+
def test_function_runs():
|
|
214
|
+
from openretailscience.segmentation import RFM
|
|
215
|
+
# BAD: No assertions
|
|
216
|
+
rfm = RFM()
|
|
217
|
+
rfm.fit(df)
|
|
218
|
+
# Test passes even if function does nothing or fails silently
|
|
219
|
+
|
|
220
|
+
def test_function_exists():
|
|
221
|
+
import openretailscience.style
|
|
222
|
+
# BAD: Only tests existence, not behavior
|
|
223
|
+
assert hasattr(openretailscience.style, 'apply_style')
|
|
224
|
+
assert callable(openretailscience.style.apply_style)
|
|
225
|
+
|
|
226
|
+
def test_always_passes():
|
|
227
|
+
from openretailscience.customer_choice import ProductAssociation
|
|
228
|
+
# BAD: Assertion can never fail
|
|
229
|
+
pa = ProductAssociation()
|
|
230
|
+
confidence = pa.get_confidence()
|
|
231
|
+
assert confidence >= 0 or confidence < 0 # Always true for any number
|
|
232
|
+
|
|
233
|
+
def test_object_creation():
|
|
234
|
+
from openretailscience.pricing import PriceElasticity
|
|
235
|
+
# BAD: Only tests that no exception is raised
|
|
236
|
+
obj = PriceElasticity()
|
|
237
|
+
assert obj is not None # Objects are never None after creation
|
|
238
|
+
|
|
239
|
+
# GOOD: Test actual behavior
|
|
240
|
+
def test_rfm_segmentation():
|
|
241
|
+
from openretailscience.segmentation import RFM
|
|
242
|
+
rfm = RFM()
|
|
243
|
+
result = rfm.fit_predict(df)
|
|
244
|
+
assert 'segment' in result.columns # Verifies actual output
|
|
245
|
+
assert len(result) == len(df) # Verifies processing
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### 7. Over-Mocked Tests
|
|
249
|
+
Tests that mock so extensively that they don't actually test real behavior or integration. When everything is mocked, you're only testing that mocks return what you told them to return.
|
|
250
|
+
|
|
251
|
+
**Red Flags:**
|
|
252
|
+
- Mocks every external dependency AND internal components
|
|
253
|
+
- Test would pass even if the actual implementation is completely broken
|
|
254
|
+
- Mocks return values that are directly asserted
|
|
255
|
+
- No real code execution happening
|
|
256
|
+
- More mock setup than actual test code
|
|
257
|
+
|
|
258
|
+
**Example:**
|
|
259
|
+
```python
|
|
260
|
+
def test_crossshop_analysis(mocker):
|
|
261
|
+
from openretailscience.crossshop import CrossShop
|
|
262
|
+
# BAD: Mocks everything, tests nothing real
|
|
263
|
+
mocker.patch('openretailscience.crossshop._validate_data', return_value=True)
|
|
264
|
+
mocker.patch('openretailscience.crossshop._calculate_matrix', return_value=pd.DataFrame())
|
|
265
|
+
mocker.patch('openretailscience.crossshop._apply_filters', return_value=pd.DataFrame())
|
|
266
|
+
mocker.patch('openretailscience.crossshop._format_output', return_value={'result': 'done'})
|
|
267
|
+
|
|
268
|
+
cs = CrossShop()
|
|
269
|
+
result = cs.fit(df)
|
|
270
|
+
assert result == {'result': 'done'} # Only tests that mock returns 'done'
|
|
271
|
+
|
|
272
|
+
def test_pricing_optimization(mocker):
|
|
273
|
+
from openretailscience.pricing import optimize_prices
|
|
274
|
+
# BAD: Nothing real is tested
|
|
275
|
+
mocker.patch('openretailscience.pricing.validate_input', return_value=True)
|
|
276
|
+
mocker.patch('openretailscience.pricing.calculate_elasticity', return_value=1.5)
|
|
277
|
+
mocker.patch('openretailscience.pricing.find_optimal', return_value=99.99)
|
|
278
|
+
mocker.patch('openretailscience.pricing.apply_constraints', return_value=95.00)
|
|
279
|
+
|
|
280
|
+
result = optimize_prices(product_data)
|
|
281
|
+
assert result == 95.00 # Just testing mock configuration
|
|
282
|
+
|
|
283
|
+
# GOOD: Mock external dependencies but test real logic
|
|
284
|
+
def test_rfm_with_real_calculation(mocker):
|
|
285
|
+
from openretailscience.segmentation import RFM
|
|
286
|
+
# Mock only data loading, not the actual RFM logic
|
|
287
|
+
mocker.patch('openretailscience.segmentation.load_data', return_value=test_df)
|
|
288
|
+
|
|
289
|
+
# Test real RFM calculation logic
|
|
290
|
+
rfm = RFM()
|
|
291
|
+
result = rfm.calculate_scores(test_df)
|
|
292
|
+
assert 'R_score' in result.columns # Tests real scoring logic
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### 8. Tests That Verify Implementation Details
|
|
296
|
+
Tests that check internal data structures, state, or "how" something works rather than "what" it produces. These tests break when refactoring even if the public behavior remains correct. Note: Testing private method BEHAVIOR (outputs) is often acceptable for complex algorithms.
|
|
297
|
+
|
|
298
|
+
**Red Flags:**
|
|
299
|
+
- Tests internal data structures or formats
|
|
300
|
+
- Verifies private attributes that track internal state
|
|
301
|
+
- Checks exact method call sequences or execution paths
|
|
302
|
+
- Tests "how" rather than "what"
|
|
303
|
+
- Would break if you change implementation but keep same behavior
|
|
304
|
+
|
|
305
|
+
**Key Distinction:**
|
|
306
|
+
- **BAD**: Testing internal state/structure → `assert obj._cache == {...}`
|
|
307
|
+
- **OK**: Testing private method behavior → `assert obj._calculate(5) == 10`
|
|
308
|
+
|
|
309
|
+
**Example:**
|
|
310
|
+
```python
|
|
311
|
+
def test_internal_cache_structure():
|
|
312
|
+
from openretailscience.style import StyleCache
|
|
313
|
+
# BAD: Tests internal data structure/state
|
|
314
|
+
cache = StyleCache()
|
|
315
|
+
cache.add_style("retail", {"color": "blue"})
|
|
316
|
+
|
|
317
|
+
# Testing HOW data is stored internally
|
|
318
|
+
assert isinstance(cache._styles, dict)
|
|
319
|
+
assert len(cache._cache_keys) == 1
|
|
320
|
+
assert cache._counter == 1 # Private state tracking
|
|
321
|
+
assert cache._styles['retail']['format'] == 'processed'
|
|
322
|
+
|
|
323
|
+
def test_rfm_algorithm_internals():
|
|
324
|
+
from openretailscience.segmentation import RFM
|
|
325
|
+
# BAD: Tests HOW algorithm works internally
|
|
326
|
+
rfm = RFM()
|
|
327
|
+
rfm.fit(customer_data)
|
|
328
|
+
|
|
329
|
+
# Testing internal algorithm mechanics
|
|
330
|
+
assert rfm._percentile_breaks == [25, 50, 75]
|
|
331
|
+
assert rfm._score_mappings == {1: 'low', 2: 'med', 3: 'high'}
|
|
332
|
+
assert rfm._calculation_order == ['R', 'F', 'M']
|
|
333
|
+
|
|
334
|
+
def test_internal_validation_calls(mocker):
|
|
335
|
+
from openretailscience.crossshop import CrossShop
|
|
336
|
+
# BAD: Tests exact execution path
|
|
337
|
+
cs = CrossShop()
|
|
338
|
+
spy = mocker.spy(cs, '_validate_columns')
|
|
339
|
+
|
|
340
|
+
cs.fit(df)
|
|
341
|
+
spy.assert_called_once_with(df.columns) # Tests call sequence
|
|
342
|
+
|
|
343
|
+
# GOOD: Test behavior/output, even of private methods
|
|
344
|
+
def test_elasticity_calculation():
|
|
345
|
+
from openretailscience.pricing import PriceElasticity
|
|
346
|
+
# GOOD: Testing WHAT a private method produces
|
|
347
|
+
pe = PriceElasticity(base_price=100)
|
|
348
|
+
elasticity = pe._calculate_point_elasticity(95, 1000, 105, 900)
|
|
349
|
+
assert abs(elasticity + 2.0) < 0.01 # Tests mathematical correctness
|
|
350
|
+
|
|
351
|
+
def test_crossshop_matrix_output():
|
|
352
|
+
from openretailscience.crossshop import CrossShop
|
|
353
|
+
# GOOD: Tests public behavior
|
|
354
|
+
cs = CrossShop(store_col="store_id")
|
|
355
|
+
result = cs.create_matrix(transaction_data)
|
|
356
|
+
assert result.shape == (n_stores, n_stores) # Tests output structure
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### 9. Tests That Could Use Parametrization
|
|
360
|
+
Multiple test functions that test the same behavior with different inputs but don't use pytest's parametrization feature. This leads to code duplication and harder maintenance.
|
|
361
|
+
|
|
362
|
+
**Use pytest.mark.parametrize for:**
|
|
363
|
+
- Tests with identical structure but different input values
|
|
364
|
+
- Multiple test cases that differ only in data
|
|
365
|
+
- Boundary value testing with multiple edge cases
|
|
366
|
+
|
|
367
|
+
**Red Flags:**
|
|
368
|
+
- Multiple test functions with identical structure
|
|
369
|
+
- Only differences are input values and expected outputs
|
|
370
|
+
- Same setup and assertion patterns
|
|
371
|
+
- Test names differ only by the values being tested
|
|
372
|
+
- Could be a single parametrized test
|
|
373
|
+
|
|
374
|
+
**Example:**
|
|
375
|
+
```python
|
|
376
|
+
# BAD: Repetitive test functions
|
|
377
|
+
def test_divide_by_two():
|
|
378
|
+
assert calculator.divide(10, 2) == 5
|
|
379
|
+
|
|
380
|
+
def test_divide_by_five():
|
|
381
|
+
assert calculator.divide(20, 5) == 4
|
|
382
|
+
|
|
383
|
+
def test_divide_by_ten():
|
|
384
|
+
assert calculator.divide(100, 10) == 10
|
|
385
|
+
|
|
386
|
+
def test_divide_negative():
|
|
387
|
+
assert calculator.divide(-10, 2) == -5
|
|
388
|
+
|
|
389
|
+
def test_validate_email_gmail():
|
|
390
|
+
assert validator.is_valid("user@gmail.com") == True
|
|
391
|
+
|
|
392
|
+
def test_validate_email_yahoo():
|
|
393
|
+
assert validator.is_valid("user@yahoo.com") == True
|
|
394
|
+
|
|
395
|
+
def test_validate_email_invalid():
|
|
396
|
+
assert validator.is_valid("not-an-email") == False
|
|
397
|
+
|
|
398
|
+
# GOOD: Use parametrize
|
|
399
|
+
@pytest.mark.parametrize("dividend,divisor,expected", [
|
|
400
|
+
(10, 2, 5),
|
|
401
|
+
(20, 5, 4),
|
|
402
|
+
(100, 10, 10),
|
|
403
|
+
(-10, 2, -5),
|
|
404
|
+
])
|
|
405
|
+
def test_divide(dividend, divisor, expected):
|
|
406
|
+
assert calculator.divide(dividend, divisor) == expected
|
|
407
|
+
|
|
408
|
+
@pytest.mark.parametrize("email,is_valid", [
|
|
409
|
+
("user@gmail.com", True),
|
|
410
|
+
("user@yahoo.com", True),
|
|
411
|
+
("not-an-email", False),
|
|
412
|
+
])
|
|
413
|
+
def test_validate_email(email, is_valid):
|
|
414
|
+
assert validator.is_valid(email) == is_valid
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### 10. Tests With Generic or Non-Descriptive Test Data
|
|
418
|
+
Tests that use generic placeholder values (like "A", "B", "C", "test", "data", "123") instead of realistic, domain-specific examples that make the test's purpose and behavior clearer.
|
|
419
|
+
|
|
420
|
+
**Example:**
|
|
421
|
+
```python
|
|
422
|
+
def test_rfm_segmentation():
|
|
423
|
+
# Generic, non-descriptive test data
|
|
424
|
+
customers = ["A", "B", "C"]
|
|
425
|
+
transactions = [
|
|
426
|
+
{"customer": "test", "amount": 100, "date": "2024-01-01"},
|
|
427
|
+
{"customer": "data", "amount": 200, "date": "2024-01-02"}
|
|
428
|
+
]
|
|
429
|
+
|
|
430
|
+
rfm = RFM()
|
|
431
|
+
result = rfm.fit_predict(transactions)
|
|
432
|
+
assert "segment" in result.columns
|
|
433
|
+
|
|
434
|
+
# Better with realistic retail data:
|
|
435
|
+
def test_rfm_segmentation():
|
|
436
|
+
# Realistic retail customer data
|
|
437
|
+
transactions = [
|
|
438
|
+
{"customer_id": "CUST_10234", "amount": 156.99, "date": "2024-01-15", "store": "Store_001"},
|
|
439
|
+
{"customer_id": "CUST_10235", "amount": 89.50, "date": "2024-01-16", "store": "Store_002"},
|
|
440
|
+
{"customer_id": "CUST_10234", "amount": 234.75, "date": "2024-02-01", "store": "Store_001"}
|
|
441
|
+
]
|
|
442
|
+
|
|
443
|
+
rfm = RFM()
|
|
444
|
+
result = rfm.fit_predict(pd.DataFrame(transactions))
|
|
445
|
+
assert "segment" in result.columns
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
**Why this matters:**
|
|
449
|
+
- Realistic data makes tests easier to understand and relate to actual use cases
|
|
450
|
+
- Helps developers quickly grasp what the test is validating
|
|
451
|
+
- Makes debugging easier when tests fail
|
|
452
|
+
- Serves as implicit documentation of expected data formats and business logic
|
|
453
|
+
- Reduces cognitive load when reading and maintaining tests
|
|
454
|
+
|
|
455
|
+
### 11. Tests That Don't Verify Their Claimed Behavior
|
|
456
|
+
Tests where the function name, docstring, or description claims to test specific behavior, but the assertions are too generic to actually verify that behavior. These tests create false confidence by appearing to test important functionality while only checking trivial outcomes.
|
|
457
|
+
|
|
458
|
+
**Red Flags:**
|
|
459
|
+
- Test name mentions specific behavior (e.g., "sorts", "validates", "filters", "transforms")
|
|
460
|
+
- Docstring describes particular functionality being tested
|
|
461
|
+
- Assertions only check return types, basic existence, or that no exceptions occurred
|
|
462
|
+
- Missing assertions that would actually verify the claimed behavior
|
|
463
|
+
- Could pass even if the specific behavior is completely broken
|
|
464
|
+
|
|
465
|
+
**Example:**
|
|
466
|
+
```python
|
|
467
|
+
def test_plot_with_unsorted_bins_list():
|
|
468
|
+
"""Test price architecture plot automatically sorts unsorted bins list."""
|
|
469
|
+
df = pd.DataFrame({
|
|
470
|
+
"unit_price": [1, 2, 3],
|
|
471
|
+
"retailer": ["Walmart", "Target", "Amazon"],
|
|
472
|
+
})
|
|
473
|
+
|
|
474
|
+
# BAD: Claims to test sorting but doesn't verify it
|
|
475
|
+
result_ax = price.plot(
|
|
476
|
+
df=df,
|
|
477
|
+
value_col="unit_price",
|
|
478
|
+
group_col="retailer",
|
|
479
|
+
bins=[3, 1, 2], # Unsorted bins - but test doesn't verify they get sorted!
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
assert isinstance(result_ax, Axes) # Only checks return type
|
|
483
|
+
|
|
484
|
+
def test_validates_email_format():
|
|
485
|
+
"""Test that email validation properly checks format."""
|
|
486
|
+
validator = EmailValidator()
|
|
487
|
+
|
|
488
|
+
# BAD: Claims to test validation but doesn't verify it
|
|
489
|
+
result = validator.validate("not-an-email")
|
|
490
|
+
assert result is not None # Doesn't check if validation actually worked
|
|
491
|
+
|
|
492
|
+
def test_filters_inactive_customers():
|
|
493
|
+
"""Test customer filtering removes inactive customers."""
|
|
494
|
+
customers = create_test_customers() # Mix of active/inactive
|
|
495
|
+
|
|
496
|
+
# BAD: Claims to test filtering but doesn't verify the filtering logic
|
|
497
|
+
filtered = CustomerFilter().filter_active(customers)
|
|
498
|
+
assert len(filtered) >= 0 # Trivial assertion, doesn't verify filtering
|
|
499
|
+
|
|
500
|
+
def test_calculates_price_elasticity():
|
|
501
|
+
"""Test price elasticity calculation returns correct values."""
|
|
502
|
+
price_data = [100, 110, 120, 130]
|
|
503
|
+
demand_data = [1000, 900, 800, 700]
|
|
504
|
+
|
|
505
|
+
# BAD: Claims to test calculation but doesn't verify correctness
|
|
506
|
+
elasticity = calculate_elasticity(price_data, demand_data)
|
|
507
|
+
assert elasticity is not None # Doesn't verify the calculation is correct
|
|
508
|
+
|
|
509
|
+
# GOOD: Actually verify the claimed behavior
|
|
510
|
+
def test_plot_with_unsorted_bins_list():
|
|
511
|
+
"""Test price architecture plot automatically sorts unsorted bins list."""
|
|
512
|
+
df = pd.DataFrame({
|
|
513
|
+
"unit_price": [1, 2, 3],
|
|
514
|
+
"retailer": ["Walmart", "Target", "Amazon"],
|
|
515
|
+
})
|
|
516
|
+
|
|
517
|
+
result_ax = price.plot(
|
|
518
|
+
df=df,
|
|
519
|
+
value_col="unit_price",
|
|
520
|
+
group_col="retailer",
|
|
521
|
+
bins=[3, 1, 2], # Unsorted bins
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
# GOOD: Verify the bins were actually sorted by checking output
|
|
525
|
+
y_labels = [label.get_text() for label in result_ax.get_yticklabels()]
|
|
526
|
+
assert "$1.0 - $2.0" in y_labels[0] # Sorted order: first bin is 1-2
|
|
527
|
+
assert "$2.0 - $3.0" in y_labels[1] # Second bin is 2-3
|
|
528
|
+
|
|
529
|
+
def test_validates_email_format():
|
|
530
|
+
"""Test that email validation properly checks format."""
|
|
531
|
+
validator = EmailValidator()
|
|
532
|
+
|
|
533
|
+
# GOOD: Actually test validation behavior
|
|
534
|
+
assert validator.validate("user@example.com") == True
|
|
535
|
+
assert validator.validate("invalid-email") == False
|
|
536
|
+
assert validator.validate("missing@") == False
|
|
537
|
+
|
|
538
|
+
def test_filters_inactive_customers():
|
|
539
|
+
"""Test customer filtering removes inactive customers."""
|
|
540
|
+
active_customers = [Customer(id=1, active=True), Customer(id=2, active=True)]
|
|
541
|
+
inactive_customers = [Customer(id=3, active=False), Customer(id=4, active=False)]
|
|
542
|
+
all_customers = active_customers + inactive_customers
|
|
543
|
+
|
|
544
|
+
# GOOD: Verify filtering actually works
|
|
545
|
+
filtered = CustomerFilter().filter_active(all_customers)
|
|
546
|
+
assert len(filtered) == 2 # Should have exactly 2 active customers
|
|
547
|
+
assert all(c.active for c in filtered) # All should be active
|
|
548
|
+
assert {c.id for c in filtered} == {1, 2} # Should be the right customers
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
**Why this matters:**
|
|
552
|
+
- Test names and docstrings serve as documentation of what functionality is tested
|
|
553
|
+
- False confidence is dangerous - tests appear to cover behavior but actually don't
|
|
554
|
+
- Bugs in the claimed functionality will go undetected
|
|
555
|
+
- Future developers may assume the behavior is tested and working correctly
|
|
556
|
+
- Makes debugging harder when the "tested" functionality actually fails
|
|
557
|
+
|
|
558
|
+
## Instructions:
|
|
559
|
+
1. **View the PR** using `gh pr view $1` and `gh pr diff $1` to understand the changes
|
|
560
|
+
2. **Optionally checkout the PR** if local examination is needed using `gh pr checkout $1` (this fetches if necessary)
|
|
561
|
+
3. **Check for Python test files** - if none exist, provide guidance on what should be tested based on the PR changes
|
|
562
|
+
4. **Identify the OpenRetailScience module under test** by analyzing imports and function calls in the test code
|
|
563
|
+
5. Review each test function in the provided PR
|
|
564
|
+
6. Categorize any problematic tests using the categories above (1-11)
|
|
565
|
+
7. **Highlight the specific problematic code** from each test function
|
|
566
|
+
8. Provide brief explanations for why each flagged test is problematic
|
|
567
|
+
9. Suggest improvements where appropriate (e.g., "combine with test_X", "use pytest.parametrize", "add actual package functionality", "use realistic retail/business data")
|
|
568
|
+
10. **When suggestions involve simple fixes, provide the corrected code**
|
|
569
|
+
11. Highlight any tests that might be salvageable with minor modifications
|
|
570
|
+
|
|
571
|
+
## Output Format:
|
|
572
|
+
```
|
|
573
|
+
## LANGUAGE: Python
|
|
574
|
+
## PACKAGE UNDER TEST: openretailscience.[module_name]
|
|
575
|
+
|
|
576
|
+
PROBLEMATIC TESTS IDENTIFIED:
|
|
577
|
+
|
|
578
|
+
### Test: test_function_name()
|
|
579
|
+
|
|
580
|
+
**Issue**: [Brief description] [Lines affected]
|
|
581
|
+
|
|
582
|
+
Problematic code: ```python
|
|
583
|
+
[highlight the specific problematic code from the test]
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
**Suggestion**: [How to improve or whether to remove]
|
|
587
|
+
|
|
588
|
+
Code fix: ```python
|
|
589
|
+
[if suggestion includes a simple fix, show the corrected code here]
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
## POSITIVE OBSERVATIONS
|
|
593
|
+
|
|
594
|
+
The following tests are well-structured and properly test the package functionality:
|
|
595
|
+
|
|
596
|
+
1. test_function_name (test code lines) - very brief description of the test
|
|
597
|
+
|
|
598
|
+
SUMMARY:
|
|
599
|
+
- Language: Python
|
|
600
|
+
- Package analyzed: openretailscience.[module_name]
|
|
601
|
+
- Total tests reviewed: [X]
|
|
602
|
+
- Problematic tests found: [X]
|
|
603
|
+
|
|
604
|
+
## Recommendations
|
|
605
|
+
|
|
606
|
+
[A numbered list of recommendations - if any]
|
|
607
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Create a report to help us improve
|
|
4
|
+
title: ''
|
|
5
|
+
labels: ''
|
|
6
|
+
assignees: ''
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
**Describe the bug**
|
|
11
|
+
A clear and concise description of what the bug is.
|
|
12
|
+
|
|
13
|
+
**To Reproduce**
|
|
14
|
+
Steps to reproduce the behavior:
|
|
15
|
+
|
|
16
|
+
1. Go to '...'
|
|
17
|
+
2. Click on '....'
|
|
18
|
+
3. Scroll down to '....'
|
|
19
|
+
4. See error
|
|
20
|
+
|
|
21
|
+
**Expected behavior**
|
|
22
|
+
A clear and concise description of what you expected to happen.
|
|
23
|
+
|
|
24
|
+
**Screenshots**
|
|
25
|
+
If applicable, add screenshots to help explain your problem.
|
|
26
|
+
|
|
27
|
+
**Desktop (please complete the following information):**
|
|
28
|
+
|
|
29
|
+
- OS: [e.g. iOS]
|
|
30
|
+
- Python Version: [e.g. 22]
|
|
31
|
+
|
|
32
|
+
**Additional context**
|
|
33
|
+
Add any other context about the problem here.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest an idea for this project
|
|
4
|
+
title: ''
|
|
5
|
+
labels: ''
|
|
6
|
+
assignees: ''
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
**Is your feature request related to a problem? Please describe.**
|
|
11
|
+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
12
|
+
|
|
13
|
+
**Describe the solution you'd like**
|
|
14
|
+
A clear and concise description of what you want to happen.
|
|
15
|
+
|
|
16
|
+
**Describe alternatives you've considered**
|
|
17
|
+
A clear and concise description of any alternative solutions or features you've considered.
|
|
18
|
+
|
|
19
|
+
**Additional context**
|
|
20
|
+
Add any other context or screenshots about the feature request here.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "uv"
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "weekly"
|
|
7
|
+
open-pull-requests-limit: 5
|
|
8
|
+
commit-message:
|
|
9
|
+
prefix: "chore(deps)"
|
|
10
|
+
prefix-development: "chore(deps-dev)"
|
|
11
|
+
groups:
|
|
12
|
+
production-dependencies:
|
|
13
|
+
dependency-type: "production"
|
|
14
|
+
development-dependencies:
|
|
15
|
+
dependency-type: "development"
|