Skip to content

Commit 0981f50

Browse files
authored
Merge pull request #449 from RailsEventStore/incremental-mutation-tests
Incremental mutation tests
2 parents c1a62be + f5edbcd commit 0981f50

File tree

4 files changed

+267
-22
lines changed

4 files changed

+267
-22
lines changed

.github/workflows/pricing.yml

Lines changed: 111 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,89 @@ on:
44
pull_request:
55
types: [opened, reopened]
66
jobs:
7+
determine_run_parameters:
8+
runs-on: ubuntu-24.04
9+
outputs:
10+
mutant_mode: ${{ steps.set_params.outputs.mutant_mode }}
11+
mutant_since_target: ${{ steps.set_params.outputs.mutant_since_target }}
12+
num_groups: ${{ steps.set_params.outputs.num_groups }}
13+
steps:
14+
- name: Determine Mutation Run Parameters
15+
id: set_params
16+
shell: bash
17+
run: |
18+
echo "--- Debug: Determining mutation run parameters for pricing ---"
19+
echo "Event name: ${{ github.event_name }}"
20+
echo "Ref name: ${{ github.ref_name }}"
21+
echo "Base ref (for PR): ${{ github.event.pull_request.base.ref }}"
22+
echo "PR title: ${{ github.event.pull_request.title }}"
23+
COMMIT_MESSAGES_LOG=$(echo "${{ join(github.event.commits.*.message, ' // ') }}" | head -c 500)
24+
echo "Commit messages in push (first 500 chars): ${COMMIT_MESSAGES_LOG}..."
25+
CONTAINS_MUTATE_FULL_IN_PUSH=$([[ "${{ contains(join(github.event.commits.*.message, ' '), '[mutate-full]') }}" == "true" ]] && echo "true" || echo "false")
26+
echo "Contains '[mutate-full]' in push commit messages: $CONTAINS_MUTATE_FULL_IN_PUSH"
27+
CONTAINS_MUTATE_FULL_IN_PR_TITLE=$([[ "${{ contains(github.event.pull_request.title, '[mutate-full]') }}" == "true" ]] && echo "true" || echo "false")
28+
echo "Contains '[mutate-full]' in PR title: $CONTAINS_MUTATE_FULL_IN_PR_TITLE"
29+
CONTAINS_MUTATE_FULL_IN_PR_BODY=$([[ "${{ contains(github.event.pull_request.body, '[mutate-full]') }}" == "true" ]] && echo "true" || echo "false")
30+
echo "Contains '[mutate-full]' in PR body: $CONTAINS_MUTATE_FULL_IN_PR_BODY"
31+
echo "---------------------------------------------"
32+
33+
FINAL_MUTANT_MODE="full"
34+
FINAL_NUM_GROUPS=32
35+
FINAL_SINCE_TARGET=""
36+
37+
IS_MUTATE_FULL_TRIGGERED="false"
38+
if [[ "${{ github.event_name }}" == "pull_request" && \
39+
( "$CONTAINS_MUTATE_FULL_IN_PR_TITLE" == "true" || "$CONTAINS_MUTATE_FULL_IN_PR_BODY" == "true" ) ]]; then
40+
echo "Logic path: [mutate-full] in PR title/body."
41+
IS_MUTATE_FULL_TRIGGERED="true"
42+
elif [[ "${{ github.event_name }}" == "push" && "$CONTAINS_MUTATE_FULL_IN_PUSH" == "true" ]]; then
43+
echo "Logic path: [mutate-full] in push commit message(s)."
44+
IS_MUTATE_FULL_TRIGGERED="true"
45+
fi
46+
47+
if [[ "$IS_MUTATE_FULL_TRIGGERED" == "true" ]]; then
48+
echo "Action: Mode set to 'full' (NUM_GROUPS=32) due to [mutate-full] trigger."
49+
FINAL_MUTANT_MODE="full"
50+
FINAL_NUM_GROUPS=32
51+
else
52+
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
53+
echo "Logic path: Pull request event (no [mutate-full] trigger)."
54+
echo "Action: Mode set to 'incremental' (NUM_GROUPS=4) for PR."
55+
FINAL_MUTANT_MODE="incremental"
56+
FINAL_NUM_GROUPS=4
57+
FINAL_SINCE_TARGET="origin/${{ github.event.pull_request.base.ref }}"
58+
echo "Incremental target: $FINAL_SINCE_TARGET"
59+
elif [[ "${{ github.event_name }}" == "push" ]]; then
60+
if [[ "${{ github.ref_name }}" == "master" || "${{ github.ref_name }}" == "main" ]]; then
61+
echo "Logic path: Push event to main branch (no [mutate-full] trigger)."
62+
echo "Action: Mode set to 'full' (NUM_GROUPS=32) for main branch."
63+
FINAL_MUTANT_MODE="full"
64+
FINAL_NUM_GROUPS=32
65+
else
66+
echo "Logic path: Push event to non-main branch ('${{ github.ref_name }}') (no [mutate-full] trigger)."
67+
echo "Action: Mode set to 'incremental' (NUM_GROUPS=4) for branch push."
68+
FINAL_MUTANT_MODE="incremental"
69+
FINAL_NUM_GROUPS=4
70+
FINAL_SINCE_TARGET="origin/master"
71+
echo "Incremental target: $FINAL_SINCE_TARGET"
72+
fi
73+
fi
74+
fi
75+
76+
echo "Debug before GITHUB_OUTPUT: FINAL_MUTANT_MODE='${FINAL_MUTANT_MODE}'"
77+
echo "Debug before GITHUB_OUTPUT: FINAL_SINCE_TARGET='${FINAL_SINCE_TARGET}'"
78+
echo "Debug before GITHUB_OUTPUT: FINAL_NUM_GROUPS='${FINAL_NUM_GROUPS}'"
79+
80+
echo "mutant_mode=${FINAL_MUTANT_MODE}" >> $GITHUB_OUTPUT
81+
echo "mutant_since_target=${FINAL_SINCE_TARGET}" >> $GITHUB_OUTPUT
82+
echo "num_groups=${FINAL_NUM_GROUPS}" >> $GITHUB_OUTPUT
83+
84+
echo "--- Final Parameters for pricing ---"
85+
echo "Mutant Mode: ${FINAL_MUTANT_MODE}"
86+
echo "Mutant Since Target: ${FINAL_SINCE_TARGET}"
87+
echo "Num Groups: ${FINAL_NUM_GROUPS}"
88+
echo "------------------------"
89+
790
test:
891
runs-on: ubuntu-24.04
992
strategy:
@@ -27,7 +110,7 @@ jobs:
27110
{
28111
attachments: [{
29112
color: '${{ job.status }}' === 'success' ? 'good' : '${{ job.status }}' === 'failure' ? 'danger' : 'warning',
30-
text: `${process.env.AS_WORKFLOW}/${{ github.job }} ${{ job.status }} in ${process.env.AS_TOOK}\n${process.env.AS_COMMIT} in ${process.env.AS_REF}`,
113+
text: `${process.env.AS_WORKFLOW}/${{ github.job }} ${{ job.status }} in ${process.env.AS_TOOK}\n${process.env.AS_COMMIT} in ${process.env.AS_REF} for pricing`,
31114
}]
32115
}
33116
env:
@@ -37,12 +120,15 @@ jobs:
37120

38121
prepare_mutation_subjects_pricing:
39122
runs-on: ubuntu-24.04
123+
needs: determine_run_parameters
40124
outputs:
41125
subject_groups: ${{ steps.split_subjects.outputs.subject_groups }}
42126
env:
43127
WORKING_DIRECTORY: ecommerce/pricing
44128
steps:
45129
- uses: actions/checkout@v3
130+
with:
131+
fetch-depth: 0
46132
- uses: ruby/setup-ruby@v1
47133
with:
48134
ruby-version: ruby-3.3.7
@@ -51,8 +137,11 @@ jobs:
51137
- name: List and split subjects for pricing
52138
id: split_subjects
53139
working-directory: ${{ env.WORKING_DIRECTORY }}
140+
env:
141+
NUM_GROUPS_FROM_CI: ${{ needs.determine_run_parameters.outputs.num_groups }}
54142
run: |
55-
SUBJECT_LIST_OUTPUT=$(RAILS_ENV=test bundle exec mutant environment subject list)
143+
echo "--- Preparing subjects for pricing ---"
144+
SUBJECT_LIST_OUTPUT=$(bundle exec mutant environment subject list)
56145
57146
mapfile -t subjects_array < <( \
58147
echo "$SUBJECT_LIST_OUTPUT" | \
@@ -69,7 +158,9 @@ jobs:
69158
fi
70159
71160
total_subjects=${#subjects_array[@]}
72-
NUM_GROUPS=32 # Define the number of parallel jobs
161+
NUM_GROUPS=${NUM_GROUPS_FROM_CI:-4}
162+
echo "Total subjects found: $total_subjects"
163+
echo "Number of parallel groups to create: $NUM_GROUPS"
73164
groups_json_array_content=""
74165
75166
for (( i=0; i<NUM_GROUPS; i++ )); do
@@ -90,9 +181,10 @@ jobs:
90181
done
91182
echo "Generated subject_groups for pricing: [$groups_json_array_content]"
92183
echo "subject_groups=[$groups_json_array_content]" >> $GITHUB_OUTPUT
184+
echo "-----------------------------------"
93185
94186
mutate:
95-
needs: prepare_mutation_subjects_pricing
187+
needs: [determine_run_parameters, prepare_mutation_subjects_pricing]
96188
if: ${{ needs.prepare_mutation_subjects_pricing.outputs.subject_groups != '[]' && needs.prepare_mutation_subjects_pricing.outputs.subject_groups != '' }}
97189
runs-on: ubuntu-24.04
98190
strategy:
@@ -101,14 +193,27 @@ jobs:
101193
subject_group: ${{ fromJson(needs.prepare_mutation_subjects_pricing.outputs.subject_groups) }}
102194
env:
103195
WORKING_DIRECTORY: ecommerce/pricing
196+
MUTANT_MODE: ${{ needs.determine_run_parameters.outputs.mutant_mode }}
197+
MUTANT_SINCE_TARGET: ${{ needs.determine_run_parameters.outputs.mutant_since_target }}
104198
steps:
105199
- uses: actions/checkout@v3
200+
with:
201+
fetch-depth: 0
106202
- uses: ruby/setup-ruby@v1
107203
with:
108204
ruby-version: ruby-3.3.7
109205
bundler-cache: true
110206
working-directory: ${{ env.WORKING_DIRECTORY }}
111-
- run: make mutate
207+
- name: Run mutation tests (parallel group for pricing)
208+
run: |
209+
echo "Debug from CI step before calling make (using PASSED_ var names):"
210+
echo " Value for PASSED_MODE='${{ env.ENV_CLI_MUTANT_MODE }}'"
211+
echo " Value for PASSED_SINCE_TARGET='${{ env.ENV_CLI_MUTANT_SINCE_TARGET }}'"
212+
echo " CI_MUTATE_SUBJECTS (from env): '${{ env.CI_MUTATE_SUBJECTS }}'"
213+
214+
make mutate \
215+
PASSED_MODE="${{ env.ENV_CLI_MUTANT_MODE }}" \
216+
PASSED_SINCE_TARGET="${{ env.ENV_CLI_MUTANT_SINCE_TARGET }}"
112217
working-directory: ${{ env.WORKING_DIRECTORY }}
113218
env:
114219
CI_MUTATE_SUBJECTS: ${{ matrix.subject_group }}
@@ -141,4 +246,4 @@ jobs:
141246
}]
142247
}
143248
env:
144-
SLACK_WEBHOOK_URL: ${{ secrets.CI_WEBHOOK }}
249+
SLACK_WEBHOOK_URL: ${{ secrets.CI_WEBHOOK }}

.github/workflows/rails_application.yml

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,85 @@ on:
44
pull_request:
55
types: [ opened, reopened ]
66
jobs:
7+
determine_run_parameters:
8+
runs-on: ubuntu-24.04
9+
outputs:
10+
mutant_mode: ${{ steps.set_params.outputs.mutant_mode }}
11+
mutant_since_target: ${{ steps.set_params.outputs.mutant_since_target }}
12+
num_groups: ${{ steps.set_params.outputs.num_groups }}
13+
steps:
14+
- name: Determine Mutation Run Parameters
15+
id: set_params
16+
shell: bash
17+
run: |
18+
echo "--- Debug: Determining mutation run parameters ---"
19+
echo "Event name: ${{ github.event_name }}"
20+
echo "Ref name: ${{ github.ref_name }}"
21+
echo "Base ref (for PR): ${{ github.event.pull_request.base.ref }}"
22+
echo "PR title: ${{ github.event.pull_request.title }}"
23+
COMMIT_MESSAGES_LOG=$(echo "${{ join(github.event.commits.*.message, ' // ') }}" | head -c 500)
24+
echo "Commit messages in push (first 500 chars): ${COMMIT_MESSAGES_LOG}..."
25+
CONTAINS_MUTATE_FULL_IN_PUSH=$([[ "${{ contains(join(github.event.commits.*.message, ' '), '[mutate-full]') }}" == "true" ]] && echo "true" || echo "false")
26+
echo "Contains '[mutate-full]' in push commit messages: $CONTAINS_MUTATE_FULL_IN_PUSH"
27+
CONTAINS_MUTATE_FULL_IN_PR_TITLE=$([[ "${{ contains(github.event.pull_request.title, '[mutate-full]') }}" == "true" ]] && echo "true" || echo "false")
28+
echo "Contains '[mutate-full]' in PR title: $CONTAINS_MUTATE_FULL_IN_PR_TITLE"
29+
CONTAINS_MUTATE_FULL_IN_PR_BODY=$([[ "${{ contains(github.event.pull_request.body, '[mutate-full]') }}" == "true" ]] && echo "true" || echo "false")
30+
echo "Contains '[mutate-full]' in PR body: $CONTAINS_MUTATE_FULL_IN_PR_BODY"
31+
echo "---------------------------------------------"
32+
33+
FINAL_MUTANT_MODE="full"
34+
FINAL_NUM_GROUPS=32
35+
FINAL_SINCE_TARGET=""
36+
37+
IS_MUTATE_FULL_TRIGGERED="false"
38+
if [[ "${{ github.event_name }}" == "pull_request" && \
39+
( "$CONTAINS_MUTATE_FULL_IN_PR_TITLE" == "true" || "$CONTAINS_MUTATE_FULL_IN_PR_BODY" == "true" ) ]]; then
40+
echo "Logic path: [mutate-full] in PR title/body."
41+
IS_MUTATE_FULL_TRIGGERED="true"
42+
elif [[ "${{ github.event_name }}" == "push" && "$CONTAINS_MUTATE_FULL_IN_PUSH" == "true" ]]; then
43+
echo "Logic path: [mutate-full] in push commit message(s)."
44+
IS_MUTATE_FULL_TRIGGERED="true"
45+
fi
46+
47+
if [[ "$IS_MUTATE_FULL_TRIGGERED" == "true" ]]; then
48+
echo "Action: Mode set to 'full' (NUM_GROUPS=32) due to [mutate-full] trigger."
49+
FINAL_MUTANT_MODE="full"
50+
FINAL_NUM_GROUPS=32
51+
else
52+
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
53+
echo "Logic path: Pull request event (no [mutate-full] trigger)."
54+
echo "Action: Mode set to 'incremental' (NUM_GROUPS=4) for PR."
55+
FINAL_MUTANT_MODE="incremental"
56+
FINAL_NUM_GROUPS=4
57+
FINAL_SINCE_TARGET="origin/${{ github.event.pull_request.base.ref }}"
58+
echo "Incremental target: $FINAL_SINCE_TARGET"
59+
elif [[ "${{ github.event_name }}" == "push" ]]; then
60+
if [[ "${{ github.ref_name }}" == "master" ]]; then
61+
echo "Logic path: Push event to master branch (no [mutate-full] trigger)."
62+
echo "Action: Mode set to 'full' (NUM_GROUPS=32) for master branch."
63+
FINAL_MUTANT_MODE="full"
64+
FINAL_NUM_GROUPS=32
65+
else
66+
echo "Logic path: Push event to non-master branch ('${{ github.ref_name }}') (no [mutate-full] trigger)."
67+
echo "Action: Mode set to 'incremental' (NUM_GROUPS=4) for branch push."
68+
FINAL_MUTANT_MODE="incremental"
69+
FINAL_NUM_GROUPS=4
70+
FINAL_SINCE_TARGET="origin/master"
71+
echo "Incremental target: $FINAL_SINCE_TARGET"
72+
fi
73+
fi
74+
fi
75+
76+
echo "mutant_mode=${FINAL_MUTANT_MODE}" >> $GITHUB_OUTPUT
77+
echo "mutant_since_target=${FINAL_SINCE_TARGET}" >> $GITHUB_OUTPUT
78+
echo "num_groups=${FINAL_NUM_GROUPS}" >> $GITHUB_OUTPUT
79+
80+
echo "--- Final Parameters ---"
81+
echo "Mutant Mode: ${FINAL_MUTANT_MODE}"
82+
echo "Mutant Since Target: ${FINAL_SINCE_TARGET}"
83+
echo "Num Groups: ${FINAL_NUM_GROUPS}"
84+
echo "------------------------"
85+
786
test:
887
runs-on: ubuntu-24.04
988
strategy:
@@ -49,6 +128,7 @@ jobs:
49128

50129
prepare_mutation_subjects_rails:
51130
runs-on: ubuntu-24.04
131+
needs: determine_run_parameters
52132
outputs:
53133
subject_groups: ${{ steps.split_subjects.outputs.subject_groups }}
54134
env:
@@ -64,6 +144,8 @@ jobs:
64144
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
65145
steps:
66146
- uses: actions/checkout@v3
147+
with:
148+
fetch-depth: 0
67149
- uses: ruby/setup-ruby@v1
68150
with:
69151
ruby-version: ruby-3.3.7
@@ -72,6 +154,8 @@ jobs:
72154
- name: List and split subjects for rails_application
73155
id: split_subjects
74156
working-directory: ${{ env.WORKING_DIRECTORY }}
157+
env:
158+
NUM_GROUPS_FROM_CI: ${{ needs.determine_run_parameters.outputs.num_groups }}
75159
run: |
76160
echo "Waiting for PostgreSQL to be ready..."
77161
until pg_isready -h localhost -p 5432 -U "postgres" -d "cqrs-es-sample-with-res_test"; do
@@ -82,9 +166,6 @@ jobs:
82166
RAILS_ENV=test bundle exec rails db:prepare
83167
84168
SUBJECT_LIST_OUTPUT=$(RAILS_ENV=test bundle exec mutant environment subject list)
85-
# Skip the first line (e.g., "Subjects in environment: 47") and read subjects into an array
86-
mapfile -t subjects_array < <(echo "$SUBJECT_LIST_OUTPUT" | tail -n +2 | sed 's/\x1b\[[0-9;]*m//g' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | awk 'NF')
87-
88169
mapfile -t subjects_array < <( \
89170
echo "$SUBJECT_LIST_OUTPUT" | \
90171
awk 'NR == 1 {next} /Run options:/ {exit} {print}' | \
@@ -100,7 +181,8 @@ jobs:
100181
fi
101182
102183
total_subjects=${#subjects_array[@]}
103-
NUM_GROUPS=32 # Define the number of parallel jobs
184+
NUM_GROUPS=${NUM_GROUPS_FROM_CI:-32}
185+
echo "Number of parallel groups to create: $NUM_GROUPS"
104186
groups_json_array_content=""
105187
106188
for (( i=0; i<NUM_GROUPS; i++ )); do
@@ -123,7 +205,7 @@ jobs:
123205
echo "subject_groups=[$groups_json_array_content]" >> $GITHUB_OUTPUT
124206
125207
mutate:
126-
needs: prepare_mutation_subjects_rails
208+
needs: [determine_run_parameters, prepare_mutation_subjects_rails]
127209
if: ${{ needs.prepare_mutation_subjects_rails.outputs.subject_groups != '[]' && needs.prepare_mutation_subjects_rails.outputs.subject_groups != '' }}
128210
runs-on: ubuntu-24.04
129211
strategy:
@@ -132,6 +214,8 @@ jobs:
132214
subject_group: ${{ fromJson(needs.prepare_mutation_subjects_rails.outputs.subject_groups) }}
133215
env:
134216
WORKING_DIRECTORY: rails_application
217+
MUTANT_MODE: ${{ needs.determine_run_parameters.outputs.mutant_mode }}
218+
MUTANT_SINCE_TARGET: ${{ needs.determine_run_parameters.outputs.mutant_since_target }}
135219
services:
136220
postgres:
137221
image: postgres:17-alpine
@@ -143,6 +227,8 @@ jobs:
143227
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
144228
steps:
145229
- uses: actions/checkout@v3
230+
with:
231+
fetch-depth: 0
146232
- uses: ruby/setup-ruby@v1
147233
with:
148234
ruby-version: ruby-3.3.7
@@ -151,7 +237,8 @@ jobs:
151237
- name: Assets Precompile
152238
working-directory: ${{ env.WORKING_DIRECTORY }}
153239
run: bundle exec rails tailwindcss:build
154-
- run: make mutate
240+
- name: Run mutation tests (parallel group)
241+
run: make mutate
155242
working-directory: ${{ env.WORKING_DIRECTORY }}
156243
env:
157244
CI_MUTATE_SUBJECTS: ${{ matrix.subject_group }}

0 commit comments

Comments
 (0)