From ae994a7e9be37bdf75152c2e60ec12b1c6f56d39 Mon Sep 17 00:00:00 2001 From: Richard Pappalardo Date: Mon, 25 Aug 2025 19:33:45 +0200 Subject: [PATCH 01/12] add workflow --- .github/actions/db-inserts-verify/action.yml | 46 ++++++++++++ .github/workflows/demo-db-insert.yaml | 76 ++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 .github/actions/db-inserts-verify/action.yml create mode 100644 .github/workflows/demo-db-insert.yaml diff --git a/.github/actions/db-inserts-verify/action.yml b/.github/actions/db-inserts-verify/action.yml new file mode 100644 index 00000000..ac5a9af5 --- /dev/null +++ b/.github/actions/db-inserts-verify/action.yml @@ -0,0 +1,46 @@ +name: Run report and verify inserts +description: Runs a command, parses ROWS_INSERTED=N, and enforces zero policy + +inputs: + report_name: + description: Logical name of the report (for messages) + required: true + run: + description: Shell command to run the report (must print ROWS_INSERTED=N) + required: true + zero_ok: + description: 'Allow zero inserts without failing (true/false)' + required: false + default: 'false' + parse_regex: + description: RegEx to capture ROWS_INSERTED=NNN (advanced override) + required: false + default: '^ROWS_INSERTED=([0-9]+)$' + +runs: + using: composite + steps: + - name: Run report + id: runreport + shell: bash + run: | + set -euo pipefail + echo "[verify] running: ${{ inputs.run }}" + bash -lc '${{ inputs.run }}' | tee report.out + # Find last ROWS_INSERTED line; default 0 if absent + count=$(grep -Eo "${{ inputs.parse_regex }}" report.out | sed -E 's/.*=([0-9]+).*//' | tail -n1 || true) + if [[ -z "${count:-}" ]]; then count=0; fi + echo "rows_inserted=${count}" >> "$GITHUB_OUTPUT" + + - name: Enforce non-zero inserts + if: steps.runreport.outputs.rows_inserted == '0' && inputs.zero_ok != 'true' + shell: bash + run: | + echo "::error::Report '${{ inputs.report_name }}' expected inserts but got 0" + exit 1 + + - name: Diagnostic (zero allowed) + if: steps.runreport.outputs.rows_inserted == '0' && inputs.zero_ok == 'true' + shell: bash + run: | + echo "::warning::Report '${{ inputs.report_name }}' inserted 0 rows (allowed)" diff --git a/.github/workflows/demo-db-insert.yaml b/.github/workflows/demo-db-insert.yaml new file mode 100644 index 00000000..c81098d6 --- /dev/null +++ b/.github/workflows/demo-db-insert.yaml @@ -0,0 +1,76 @@ +name: Testops Check All Jobs on Push + +on: + pull_request: + push: + branches: + - rpapa-db-verify + workflow_dispatch: + inputs: + branchName: + description: 'Default branch' + required: true + default: 'master' + +jobs: + deploy: + name: Testops Check All Jobs on Push + runs-on: ubuntu-latest + + steps: + - name: Check out source repository + uses: actions/checkout@v4 + + - name: Setup python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Establish Cloud SQL Proxy + uses: mattes/gce-cloudsql-proxy-action@v1 + with: + creds: ${{ secrets.GCLOUD_AUTH }} + instance: ${{ secrets.CLOUD_SQL_CONNECTION_NAME }} + port: ${{ secrets.CLOUD_SQL_DATABASE_PORT }} + + - name: Install requirements + run: | + pip install -r requirements.txt + + - name: Set env vars + run: | + echo "CLOUD_SQL_DATABASE_USERNAME=${{ secrets.CLOUD_SQL_DATABASE_USERNAME }}" >> $GITHUB_ENV + echo "CLOUD_SQL_DATABASE_PASSWORD=${{ secrets.CLOUD_SQL_DATABASE_PASSWORD }}" >> $GITHUB_ENV + echo "CLOUD_SQL_DATABASE_NAME=preflight" >> $GITHUB_ENV + echo "CLOUD_SQL_DATABASE_PORT=${{ secrets.CLOUD_SQL_DATABASE_PORT }}" >> $GITHUB_ENV + echo "TESTRAIL_HOST=${{ secrets.TESTRAIL_HOST }}" >> $GITHUB_ENV + echo "TESTRAIL_USERNAME=${{ secrets.TESTRAIL_USERNAME }}" >> $GITHUB_ENV + echo "TESTRAIL_PASSWORD=${{ secrets.TESTRAIL_PASSWORD }}" >> $GITHUB_ENV + echo "ATLASSIAN_API_TOKEN=${{ secrets.ATLASSIAN_API_TOKEN }}" >> $GITHUB_ENV + echo "ATLASSIAN_HOST=${{ secrets.ATLASSIAN_HOST }}" >> $GITHUB_ENV + echo "ATLASSIAN_USERNAME=${{ secrets.ATLASSIAN_USERNAME }}" >> $GITHUB_ENV + echo "JIRA_HOST=${{ secrets.JIRA_HOST }}" >> $GITHUB_ENV + echo "JIRA_USER=${{ secrets.JIRA_USER }}" >> $GITHUB_ENV + echo "JIRA_PASSWORD=${{ secrets.JIRA_PASSWORD }}" >> $GITHUB_ENV + echo "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV + echo "BUGZILLA_API_KEY=${{ secrets.BUGZILLA_API_KEY }}" >> $GITHUB_ENV + echo "BITRISE_HOST=${{ secrets.BITRISE_HOST }}" >> $GITHUB_ENV + echo "BITRISE_APP_SLUG=${{ secrets.BITRISE_APP_SLUG }}" >> $GITHUB_ENV + echo "BITRISE_TOKEN=${{ secrets.BITRISE_TOKEN }}" >> $GITHUB_ENV + echo "SENTRY_HOST=${{ secrets.SENTRY_HOST }}" >> $GITHUB_ENV + echo "SENTRY_API_TOKEN=${{ secrets.SENTRY_API_TOKEN_CSO }}" >> $GITHUB_ENV + echo "SENTRY_ORGANIZATION_SLUG=${{ secrets.SENTRY_ORGANIZATION_SLUG }}" >> $GITHUB_ENV + echo "SENTRY_PROJECT_ID=${{ secrets.SENTRY_PROJECT_ID }}" >> $GITHUB_ENV + + # TestRail + - name: Mobile Test Case Coverage & DB verify + uses: ./.github/actions/db-inserts-verify + with: + report_name: Mobile Test Case Coverage + run: python ./__main__.py --report-type testrail-test-case-coverage --platform mobile --project ALL + zero_ok: 'false' + require_delete: 'false' + + #- name: Mobile testrail milestones + # run: python3 ./__main__.py --platform mobile --project ALL --report-type testrail-milestones + From 0c315dc0b207f86df2d1819d768cf6fb0c0fc557 Mon Sep 17 00:00:00 2001 From: Richard Pappalardo Date: Mon, 25 Aug 2025 19:36:22 +0200 Subject: [PATCH 02/12] change workflow title --- .github/workflows/demo-db-insert.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/demo-db-insert.yaml b/.github/workflows/demo-db-insert.yaml index c81098d6..f24cf756 100644 --- a/.github/workflows/demo-db-insert.yaml +++ b/.github/workflows/demo-db-insert.yaml @@ -1,4 +1,4 @@ -name: Testops Check All Jobs on Push +name: Demo - Verify DB insert / delete on: pull_request: From c21629f5537fde19e37fdad751234c502b6273d5 Mon Sep 17 00:00:00 2001 From: Richard Pappalardo Date: Mon, 25 Aug 2025 20:07:00 +0200 Subject: [PATCH 03/12] fix workflow --- .github/actions/db-inserts-verify/action.yml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/actions/db-inserts-verify/action.yml b/.github/actions/db-inserts-verify/action.yml index ac5a9af5..acfcf31c 100644 --- a/.github/actions/db-inserts-verify/action.yml +++ b/.github/actions/db-inserts-verify/action.yml @@ -15,7 +15,7 @@ inputs: parse_regex: description: RegEx to capture ROWS_INSERTED=NNN (advanced override) required: false - default: '^ROWS_INSERTED=([0-9]+)$' + default: 'ROWS_INSERTED\s*=\s*([0-9]+)' runs: using: composite @@ -26,17 +26,22 @@ runs: run: | set -euo pipefail echo "[verify] running: ${{ inputs.run }}" - bash -lc '${{ inputs.run }}' | tee report.out - # Find last ROWS_INSERTED line; default 0 if absent - count=$(grep -Eo "${{ inputs.parse_regex }}" report.out | sed -E 's/.*=([0-9]+).*//' | tail -n1 || true) + bash -lc '${{ inputs.run }}' 2>&1 | tee report.out + count="$(grep -Eo "${{ inputs.parse_regex }}" report.out | sed -E 's/.*=([0-9]+).*/\1/' | tail -n1 || true)" if [[ -z "${count:-}" ]]; then count=0; fi echo "rows_inserted=${count}" >> "$GITHUB_OUTPUT" + - name: Show parsed count + shell: bash + run: echo "[verify] parsed rows_inserted=${{ steps.runreport.outputs.rows_inserted }}" + - name: Enforce non-zero inserts if: steps.runreport.outputs.rows_inserted == '0' && inputs.zero_ok != 'true' shell: bash run: | echo "::error::Report '${{ inputs.report_name }}' expected inserts but got 0" + echo "[verify] rows_inserted=0; showing last 200 lines of report.out for debugging" + tail -n 200 report.out || true exit 1 - name: Diagnostic (zero allowed) @@ -44,3 +49,5 @@ runs: shell: bash run: | echo "::warning::Report '${{ inputs.report_name }}' inserted 0 rows (allowed)" + echo "[verify] rows_inserted=0; showing last 100 lines of report.out for context" + tail -n 100 report.out || true From 41395df5efbf7f9e028a08806d8e53b0cc0a0588 Mon Sep 17 00:00:00 2001 From: Richard Pappalardo Date: Tue, 26 Aug 2025 14:22:57 +0200 Subject: [PATCH 04/12] comment out print --- api/testrail/api_testrail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/testrail/api_testrail.py b/api/testrail/api_testrail.py index 45af0fd1..3655037f 100644 --- a/api/testrail/api_testrail.py +++ b/api/testrail/api_testrail.py @@ -210,7 +210,7 @@ def testrail_coverage_update(self, projects_id, # Format and store data in a data payload array payload = self.db.report_test_coverage_payload(cases) - print(payload) + #print(payload) # Insert data in 'totals' array into DB self.db.report_test_coverage_insert(projects_id, payload) From 36f779008eb7ef1a9a44c90d55b4bc44b3aef9c9 Mon Sep 17 00:00:00 2001 From: Richard Pappalardo Date: Tue, 26 Aug 2025 14:32:19 +0200 Subject: [PATCH 05/12] remove more prints, fix warn --- .github/workflows/demo-db-insert.yaml | 1 - api/testrail/api_testrail.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/demo-db-insert.yaml b/.github/workflows/demo-db-insert.yaml index f24cf756..92a25f38 100644 --- a/.github/workflows/demo-db-insert.yaml +++ b/.github/workflows/demo-db-insert.yaml @@ -69,7 +69,6 @@ jobs: report_name: Mobile Test Case Coverage run: python ./__main__.py --report-type testrail-test-case-coverage --platform mobile --project ALL zero_ok: 'false' - require_delete: 'false' #- name: Mobile testrail milestones # run: python3 ./__main__.py --platform mobile --project ALL --report-type testrail-milestones diff --git a/api/testrail/api_testrail.py b/api/testrail/api_testrail.py index 3655037f..dae5a2f9 100644 --- a/api/testrail/api_testrail.py +++ b/api/testrail/api_testrail.py @@ -142,7 +142,7 @@ def data_pump_report_test_case_coverage(self, project='all', suite='all'): # convert inputs to a list so we can easily # loop thru them project_ids_list = self.testrail_project_ids(project) - print(project_ids_list) + #print(project_ids_list) # TODO: # currently only setup for test_case report # fix this for test run data From 04849b2c8aa5a12c5b0ea81d289b629e866e2488 Mon Sep 17 00:00:00 2001 From: Richard Pappalardo Date: Tue, 26 Aug 2025 15:03:26 +0200 Subject: [PATCH 06/12] diagnostic print --- api/testrail/api_testrail.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/testrail/api_testrail.py b/api/testrail/api_testrail.py index dae5a2f9..22da4aec 100644 --- a/api/testrail/api_testrail.py +++ b/api/testrail/api_testrail.py @@ -213,6 +213,8 @@ def testrail_coverage_update(self, projects_id, #print(payload) # Insert data in 'totals' array into DB + print("DIAGNOSTIC: inserting coverage data now") + self.db.report_test_coverage_insert(projects_id, payload) def testrail_run_counts_update(self, project, num_days): From a61f0d43cfd7544aa109606b4a6941ede2b33b61 Mon Sep 17 00:00:00 2001 From: Richard Pappalardo Date: Tue, 26 Aug 2025 19:15:10 +0200 Subject: [PATCH 07/12] fix workflow --- .github/actions/db-inserts-verify/action.yml | 14 ++++++++++++-- .github/workflows/demo-db-insert.yaml | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/actions/db-inserts-verify/action.yml b/.github/actions/db-inserts-verify/action.yml index acfcf31c..4868e6b0 100644 --- a/.github/actions/db-inserts-verify/action.yml +++ b/.github/actions/db-inserts-verify/action.yml @@ -14,7 +14,7 @@ inputs: default: 'false' parse_regex: description: RegEx to capture ROWS_INSERTED=NNN (advanced override) - required: false + required: '' default: 'ROWS_INSERTED\s*=\s*([0-9]+)' runs: @@ -27,7 +27,17 @@ runs: set -euo pipefail echo "[verify] running: ${{ inputs.run }}" bash -lc '${{ inputs.run }}' 2>&1 | tee report.out - count="$(grep -Eo "${{ inputs.parse_regex }}" report.out | sed -E 's/.*=([0-9]+).*/\1/' | tail -n1 || true)" + + + # Pick regex + regex="${{ inputs.parse_regex }}" + if [[ -z "$regex" ]]; then + # Default: match common formats + regex='([0-9]+)[[:space:]]*(rows inserted|total inserted|committed|Inserted|ROWS_INSERTED=)' + fi + + # Find last match + count="$(grep -Eo "${{ inputs.parse_regex }}" report.out | sed -E 's/.*=([0-9]+).*/\1/' | tail -n1 || true)" if [[ -z "${count:-}" ]]; then count=0; fi echo "rows_inserted=${count}" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/demo-db-insert.yaml b/.github/workflows/demo-db-insert.yaml index 92a25f38..64cd7c67 100644 --- a/.github/workflows/demo-db-insert.yaml +++ b/.github/workflows/demo-db-insert.yaml @@ -14,7 +14,7 @@ on: jobs: deploy: - name: Testops Check All Jobs on Push + name: Demo - Verify DB insert / delete runs-on: ubuntu-latest steps: From c20994d5a06ac8d0d8e8d78085218ffde8e05295 Mon Sep 17 00:00:00 2001 From: Richard Pappalardo Date: Wed, 27 Aug 2025 11:55:31 +0200 Subject: [PATCH 08/12] fix logging --- api/testrail/api_testrail.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/testrail/api_testrail.py b/api/testrail/api_testrail.py index 22da4aec..c778563c 100644 --- a/api/testrail/api_testrail.py +++ b/api/testrail/api_testrail.py @@ -620,6 +620,7 @@ def report_test_coverage_insert(self, projects_id, payload): # TODO: Error on insert # insert data from totals into report_test_coverage table + rows = len(payload) for index, row in payload.iterrows(): """ # Diagnostic @@ -646,6 +647,7 @@ def report_test_coverage_insert(self, projects_id, payload): ) self.session.add(report) self.session.commit() + print(f'ROWS_INSERTED={rows}') def report_testrail_users_insert(self, payload): for index, row in payload.iterrows(): From 7ff7c6160e30982f8b58470146ce2c7e08811651 Mon Sep 17 00:00:00 2001 From: Richard Pappalardo Date: Wed, 27 Aug 2025 13:40:38 +0200 Subject: [PATCH 09/12] negative test --- api/testrail/api_testrail.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/testrail/api_testrail.py b/api/testrail/api_testrail.py index c778563c..02d43c3e 100644 --- a/api/testrail/api_testrail.py +++ b/api/testrail/api_testrail.py @@ -647,7 +647,8 @@ def report_test_coverage_insert(self, projects_id, payload): ) self.session.add(report) self.session.commit() - print(f'ROWS_INSERTED={rows}') + #print(f'ROWS_INSERTED={rows}') + print(f'ROWS_INSERTED=0') def report_testrail_users_insert(self, payload): for index, row in payload.iterrows(): From e50238db9b1d98bf60acce8dc6123ab80aa65b78 Mon Sep 17 00:00:00 2001 From: Richard Pappalardo Date: Wed, 27 Aug 2025 13:55:56 +0200 Subject: [PATCH 10/12] negative test 2 --- .github/actions/db-inserts-verify/action.yml | 6 +----- api/testrail/api_testrail.py | 5 +++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/actions/db-inserts-verify/action.yml b/.github/actions/db-inserts-verify/action.yml index 4868e6b0..8c64fd66 100644 --- a/.github/actions/db-inserts-verify/action.yml +++ b/.github/actions/db-inserts-verify/action.yml @@ -30,11 +30,7 @@ runs: # Pick regex - regex="${{ inputs.parse_regex }}" - if [[ -z "$regex" ]]; then - # Default: match common formats - regex='([0-9]+)[[:space:]]*(rows inserted|total inserted|committed|Inserted|ROWS_INSERTED=)' - fi + regex='ROWS_INSERTED=([0-9]+)' # Find last match count="$(grep -Eo "${{ inputs.parse_regex }}" report.out | sed -E 's/.*=([0-9]+).*/\1/' | tail -n1 || true)" diff --git a/api/testrail/api_testrail.py b/api/testrail/api_testrail.py index 02d43c3e..8e27eca6 100644 --- a/api/testrail/api_testrail.py +++ b/api/testrail/api_testrail.py @@ -647,8 +647,7 @@ def report_test_coverage_insert(self, projects_id, payload): ) self.session.add(report) self.session.commit() - #print(f'ROWS_INSERTED={rows}') - print(f'ROWS_INSERTED=0') + print(f'ROWS_INSERTED={rows}') def report_testrail_users_insert(self, payload): for index, row in payload.iterrows(): @@ -661,6 +660,7 @@ def report_testrail_users_insert(self, payload): ) self.session.add(report) self.session.commit() + print(f'ROWS_INSERTED={rows}') def report_test_run_payload(self, runs): """pack testrail data for 1 run in a data array @@ -724,6 +724,7 @@ def report_test_plans_insert(self, project_id, payload): self.session.add(report) self.session.commit() total['id'] = report.id + print(f'ROWS_INSERTED={rows}') return payload def report_testrail_test_result_insert(self, db_run_id, payload, type): From 1c414656f24b4a66f6d6037abf403730b01f29e2 Mon Sep 17 00:00:00 2001 From: Richard Pappalardo Date: Thu, 28 Aug 2025 11:28:46 +0200 Subject: [PATCH 11/12] test new batch insert --- api/testrail/api_testrail.py | 51 ++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/api/testrail/api_testrail.py b/api/testrail/api_testrail.py index 8e27eca6..8d52e442 100644 --- a/api/testrail/api_testrail.py +++ b/api/testrail/api_testrail.py @@ -616,6 +616,7 @@ def report_test_coverage_payload(self, cases): .reset_index() ) + ''' def report_test_coverage_insert(self, projects_id, payload): # TODO: Error on insert # insert data from totals into report_test_coverage table @@ -648,6 +649,56 @@ def report_test_coverage_insert(self, projects_id, payload): self.session.add(report) self.session.commit() print(f'ROWS_INSERTED={rows}') + ''' + + + # ----- + def report_test_coverage_insert(self, projects_id, payload): + """ + Insert data from totals into report_test_coverage table. + Uses a single bulk insert for performance. + DEBUG ONLY: + If you need to troubleshoot dirty rows causing failures, + uncomment the row-by-row section at the bottom of this function. + """ + try: + reports = [ + ReportTestCaseCoverage( + projects_id=projects_id, + testrail_test_suites_id=row["suit"], + test_automation_status_id=row["status"], + test_automation_coverage_id=row["cov"], + test_sub_suites_id=row["sub"], + test_count=row["tally"], + ) + for _, row in payload.iterrows() + ] + self.session.bulk_save_objects(reports) + self.session.commit() + print(f"ROWS_INSERTED={len(reports)}") + except Exception as e: + self.session.rollback() + print(f"[error] Bulk insert failed: {e}") + # DEBUG ONLY: Uncomment this block to identify bad rows + """ + for idx, row in payload.iterrows(): + try: + report = ReportTestCaseCoverage( + projects_id=projects_id, + testrail_test_suites_id=row["suit"], + test_automation_status_id=row["status"], + test_automation_coverage_id=row["cov"], + test_sub_suites_id=row["sub"], + test_count=row["tally"], + ) + self.session.add(report) + self.session.commit() + except Exception as row_e: + self.session.rollback() + print(f"[debug] Row {idx} failed: {row_e}") + break + """ + # ----- def report_testrail_users_insert(self, payload): for index, row in payload.iterrows(): From 3cf4ef90959c84fc7854fdc885fe41d1f15ed829 Mon Sep 17 00:00:00 2001 From: Richard Pappalardo Date: Thu, 28 Aug 2025 13:48:10 +0200 Subject: [PATCH 12/12] test new batch insert, 2 --- api/testrail/api_testrail.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/api/testrail/api_testrail.py b/api/testrail/api_testrail.py index 8d52e442..0c85697d 100644 --- a/api/testrail/api_testrail.py +++ b/api/testrail/api_testrail.py @@ -143,12 +143,9 @@ def data_pump_report_test_case_coverage(self, project='all', suite='all'): # loop thru them project_ids_list = self.testrail_project_ids(project) #print(project_ids_list) - # TODO: - # currently only setup for test_case report - # fix this for test run data - # Test suite data is dynamic. Wipe out old test suite data - # in database before updating. + # Test suite data is dynamic. Wipe out old test suite data in database + # before updating. self.db.test_suites_delete() for project_ids in project_ids_list: @@ -213,8 +210,6 @@ def testrail_coverage_update(self, projects_id, #print(payload) # Insert data in 'totals' array into DB - print("DIAGNOSTIC: inserting coverage data now") - self.db.report_test_coverage_insert(projects_id, payload) def testrail_run_counts_update(self, project, num_days): @@ -675,6 +670,7 @@ def report_test_coverage_insert(self, projects_id, payload): ] self.session.bulk_save_objects(reports) self.session.commit() + print("DIAGNOSTIC: batch insert!") print(f"ROWS_INSERTED={len(reports)}") except Exception as e: self.session.rollback() @@ -711,7 +707,6 @@ def report_testrail_users_insert(self, payload): ) self.session.add(report) self.session.commit() - print(f'ROWS_INSERTED={rows}') def report_test_run_payload(self, runs): """pack testrail data for 1 run in a data array @@ -775,7 +770,6 @@ def report_test_plans_insert(self, project_id, payload): self.session.add(report) self.session.commit() total['id'] = report.id - print(f'ROWS_INSERTED={rows}') return payload def report_testrail_test_result_insert(self, db_run_id, payload, type):