This is an automated email from the ASF dual-hosted git repository. jli pushed a commit to branch fix-master-ci in repository https://gitbox.apache.org/repos/asf/superset.git
commit 289aa1ebcf3c2265b46f9ec7451bc8658dbd6e08 Author: Joe Li <[email protected]> AuthorDate: Wed Mar 4 22:17:11 2026 -0800 test(DatasourceControl): add regression guards for OOM fix Prevent re-introducing the OOM crash fixed in fe5f0b0 by adding: - fetchMock unmatched call assertion in afterEach (catches leaked routes) - pre-commit hook banning mockImplementationOnce in DatasourceControl tests - scoped ESLint await-async-events rule for this test file - CI heap-check job with 520MB threshold, mockImpl guard, and await guard Co-Authored-By: Claude Opus 4.6 <[email protected]> --- .github/workflows/superset-frontend.yml | 63 ++++++++++++++++++++++ .pre-commit-config.yaml | 6 +++ superset-frontend/.eslintrc.js | 11 ++++ .../DatasourceControl/DatasourceControl.test.tsx | 13 ++++- 4 files changed, 92 insertions(+), 1 deletion(-) diff --git a/.github/workflows/superset-frontend.yml b/.github/workflows/superset-frontend.yml index db22843e30..59d9d51650 100644 --- a/.github/workflows/superset-frontend.yml +++ b/.github/workflows/superset-frontend.yml @@ -95,6 +95,69 @@ jobs: name: coverage-artifacts-${{ matrix.shard }} path: superset-frontend/coverage + heap-check-datasource-control: + needs: frontend-build + if: needs.frontend-build.outputs.should-run == 'true' + runs-on: ubuntu-24.04 + steps: + - name: Download Docker Image Artifact + uses: actions/download-artifact@v8 + with: + name: docker-image + + - name: Load Docker Image + run: docker load < docker-image.tar.gz + + - name: Heap check DatasourceControl test (must stay under 520 MB) + run: | + docker run --rm $TAG bash -c ' + node --max-old-space-size=8192 \ + node_modules/.bin/jest \ + --logHeapUsage \ + --maxWorkers=1 \ + --testPathPatterns="DatasourceControl\.test" \ + 2>&1 | tee /tmp/heap-output.txt + + max_heap=$(grep -oP "\d+ MB heap size" /tmp/heap-output.txt \ + | grep -oP "\d+" | sort -n | tail -1) + + echo "Peak heap usage: ${max_heap:-unknown} MB" + + if [ -z "$max_heap" ]; then + echo "WARNING: Could not parse heap usage" + exit 0 + fi + + if [ "$max_heap" -gt 520 ]; then + echo "FAIL: Heap usage (${max_heap} MB) exceeds 520 MB threshold." + echo "The DatasourceEditor mock may have been removed." + exit 1 + fi + + echo "PASS: Heap usage within threshold" + ' + + - name: Guard against mockImplementationOnce + run: | + docker run --rm $TAG bash -c ' + if grep -rn "mockImplementationOnce" src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx; then + echo "FAIL: mockImplementationOnce is banned in this file" + exit 1 + fi + echo "PASS: No mockImplementationOnce found" + ' + + - name: Guard against un-awaited userEvent + run: | + docker run --rm $TAG bash -c ' + FILE=src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx + if grep -Pn "(?<!await\s)userEvent\.\w+\(" "$FILE" | grep -v "import\|//"; then + echo "FAIL: All userEvent calls must be awaited" + exit 1 + fi + echo "PASS: All userEvent calls are awaited" + ' + report-coverage: needs: [sharded-jest-tests] if: needs.frontend-build.outputs.should-run == 'true' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a15303b2a3..30768f570d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -67,6 +67,12 @@ repos: language: system pass_filenames: true files: ^superset-frontend/.*\.(js|jsx|ts|tsx|css|scss|sass|json)$ + - id: no-mock-implementation-once-datasource + name: ban mockImplementationOnce in DatasourceControl tests + entry: bash -c 'for f in "$@"; do if echo "$f" | grep -q "DatasourceControl.test"; then if grep -n "mockImplementationOnce" "$f"; then echo "[ERROR] mockImplementationOnce is banned in DatasourceControl.test.tsx"; echo "Use scoped jest.spyOn(...).mockImplementation(...) instead."; exit 1; fi; fi; done' + language: system + pass_filenames: true + files: DatasourceControl\.test\.(ts|tsx)$ - repo: local hooks: - id: oxlint-frontend diff --git a/superset-frontend/.eslintrc.js b/superset-frontend/.eslintrc.js index db01c79e74..c56b598e01 100644 --- a/superset-frontend/.eslintrc.js +++ b/superset-frontend/.eslintrc.js @@ -515,6 +515,17 @@ module.exports = { ], }, }, + { + files: [ + 'src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx', + ], + rules: { + 'testing-library/await-async-events': [ + 'error', + { eventModule: 'userEvent' }, + ], + }, + }, ], ignorePatterns, }; diff --git a/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx b/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx index f5dfca9739..5b319e3d08 100644 --- a/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx +++ b/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.tsx @@ -54,6 +54,15 @@ beforeEach(() => { afterEach(() => { window.location = originalLocation; + + const unmatched = fetchMock.callHistory.calls('unmatched'); + if (unmatched.length > 0) { + const urls = unmatched.map(call => call.url).join(', '); + throw new Error( + `fetchMock: ${unmatched.length} unmatched call(s): ${urls}`, + ); + } + fetchMock.clearHistory().removeRoutes(); jest.restoreAllMocks(); }); @@ -284,7 +293,9 @@ test('Click on Edit dataset', async () => { await userEvent.click(screen.getByText('Edit dataset')); - expect(await screen.findByTestId('mock-datasource-editor')).toBeInTheDocument(); + expect( + await screen.findByTestId('mock-datasource-editor'), + ).toBeInTheDocument(); }); test('Edit dataset should be disabled when user is not admin', async () => {
