Update GitHub Actions dependencies to latest versions #68
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # SPDX-License-Identifier: MIT | |
| # Copyright (c) 2026 Tom F. (https://github.com/tomtom215/duckdb-behavioral) | |
| name: E2E Tests | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| # Cancel redundant runs on the same branch/PR | |
| concurrency: | |
| group: e2e-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUSTFLAGS: -Dwarnings | |
| RUST_BACKTRACE: 1 | |
| DUCKDB_VERSION: "v1.4.4" | |
| jobs: | |
| e2e-test: | |
| name: E2E (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 30 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| platform: linux_amd64 | |
| duckdb_asset: duckdb_cli-linux-amd64.zip | |
| - os: macos-latest | |
| platform: osx_arm64 | |
| duckdb_asset: duckdb_cli-osx-universal.zip | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| submodules: recursive | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 | |
| with: | |
| key: e2e-${{ matrix.platform }} | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.12" | |
| # Build the extension using the community extension Makefile | |
| - name: Configure extension build | |
| run: make configure | |
| - name: Build release extension | |
| run: make release | |
| # Run SQL integration tests via the DuckDB test runner | |
| - name: Run SQL integration tests | |
| run: make test_release | |
| # Run extended E2E tests using DuckDB CLI | |
| - name: Install DuckDB CLI | |
| run: | | |
| curl -sSL "https://github.com/duckdb/duckdb/releases/download/${{ env.DUCKDB_VERSION }}/${{ matrix.duckdb_asset }}" -o duckdb.zip | |
| unzip duckdb.zip | |
| chmod +x duckdb | |
| echo "DuckDB version: $(./duckdb --version)" | |
| - name: Verify extension loads | |
| run: | | |
| ./duckdb -unsigned -c " | |
| LOAD 'build/release/behavioral.duckdb_extension'; | |
| SELECT 'Extension loaded successfully' AS status; | |
| " | |
| - name: E2E - sessionize | |
| run: | | |
| ./duckdb -unsigned -c " | |
| LOAD 'build/release/behavioral.duckdb_extension'; | |
| CREATE TABLE events AS SELECT * FROM (VALUES | |
| (TIMESTAMP '2024-01-01 10:00:00'), | |
| (TIMESTAMP '2024-01-01 10:05:00'), | |
| (TIMESTAMP '2024-01-01 10:45:00'), | |
| (TIMESTAMP '2024-01-01 11:30:00') | |
| ) AS t(ts); | |
| SELECT ts, sessionize(ts, INTERVAL '30 minutes') OVER (ORDER BY ts) AS session_id | |
| FROM events | |
| ORDER BY ts; | |
| " | |
| - name: E2E - retention | |
| run: | | |
| ./duckdb -unsigned -c " | |
| LOAD 'build/release/behavioral.duckdb_extension'; | |
| CREATE TABLE user_actions AS SELECT * FROM (VALUES | |
| (1, true, true, false), | |
| (1, true, false, true), | |
| (2, true, false, false), | |
| (2, false, true, false) | |
| ) AS t(user_id, day0, day1, day7); | |
| SELECT user_id, retention(day0, day1, day7) | |
| FROM user_actions | |
| GROUP BY user_id | |
| ORDER BY user_id; | |
| " | |
| - name: E2E - window_funnel | |
| run: | | |
| ./duckdb -unsigned -c " | |
| LOAD 'build/release/behavioral.duckdb_extension'; | |
| CREATE TABLE funnel_events AS SELECT * FROM (VALUES | |
| (1, TIMESTAMP '2024-01-01 10:00:00', 'view'), | |
| (1, TIMESTAMP '2024-01-01 10:05:00', 'cart'), | |
| (1, TIMESTAMP '2024-01-01 10:10:00', 'purchase'), | |
| (2, TIMESTAMP '2024-01-01 10:00:00', 'view'), | |
| (2, TIMESTAMP '2024-01-01 10:05:00', 'cart') | |
| ) AS t(user_id, ts, event); | |
| SELECT user_id, window_funnel(INTERVAL '1 hour', ts, | |
| event = 'view', event = 'cart', event = 'purchase') | |
| FROM funnel_events | |
| GROUP BY user_id | |
| ORDER BY user_id; | |
| " | |
| - name: E2E - window_funnel with modes | |
| run: | | |
| ./duckdb -unsigned -c " | |
| LOAD 'build/release/behavioral.duckdb_extension'; | |
| CREATE TABLE mode_events AS SELECT * FROM (VALUES | |
| (TIMESTAMP '2024-01-01 10:00:00', true, false), | |
| (TIMESTAMP '2024-01-01 10:00:00', false, true), | |
| (TIMESTAMP '2024-01-01 10:05:00', true, false) | |
| ) AS t(ts, step1, step2); | |
| SELECT window_funnel(INTERVAL '1 hour', 'strict_increase', ts, step1, step2) | |
| FROM mode_events; | |
| " | |
| - name: E2E - sequence_match and sequence_count | |
| run: | | |
| ./duckdb -unsigned -c " | |
| LOAD 'build/release/behavioral.duckdb_extension'; | |
| CREATE TABLE seq_events AS SELECT * FROM (VALUES | |
| (TIMESTAMP '2024-01-01 10:00:00', true, false, false), | |
| (TIMESTAMP '2024-01-01 10:05:00', false, true, false), | |
| (TIMESTAMP '2024-01-01 10:10:00', false, false, true) | |
| ) AS t(ts, c1, c2, c3); | |
| SELECT | |
| sequence_match('(?1).*(?2).*(?3)', ts, c1, c2, c3) AS matched, | |
| sequence_count('(?1)(?2)', ts, c1, c2, c3) AS count | |
| FROM seq_events; | |
| " | |
| - name: E2E - sequence_match_events | |
| run: | | |
| ./duckdb -unsigned -c " | |
| LOAD 'build/release/behavioral.duckdb_extension'; | |
| CREATE TABLE sme_events AS SELECT * FROM (VALUES | |
| (TIMESTAMP '2024-01-01 10:00:00', true, false), | |
| (TIMESTAMP '2024-01-01 10:05:00', false, true) | |
| ) AS t(ts, c1, c2); | |
| SELECT sequence_match_events('(?1)(?2)', ts, c1, c2) | |
| FROM sme_events; | |
| " | |
| - name: E2E - sequence_next_node | |
| run: | | |
| ./duckdb -unsigned -c " | |
| LOAD 'build/release/behavioral.duckdb_extension'; | |
| CREATE TABLE snn_events AS SELECT * FROM (VALUES | |
| (TIMESTAMP '2024-01-01 10:00:00', 'home', true, true, false), | |
| (TIMESTAMP '2024-01-01 10:01:00', 'product', true, false, true), | |
| (TIMESTAMP '2024-01-01 10:02:00', 'cart', false, false, false) | |
| ) AS t(ts, page, base_cond, e1, e2); | |
| SELECT sequence_next_node('forward', 'first_match', ts, page, base_cond, e1, e2) | |
| FROM snn_events; | |
| " | |
| - name: E2E - GROUP BY aggregation | |
| run: | | |
| ./duckdb -unsigned -c " | |
| LOAD 'build/release/behavioral.duckdb_extension'; | |
| CREATE TABLE grouped AS SELECT * FROM (VALUES | |
| (1, TIMESTAMP '2024-01-01 10:00:00', true, false), | |
| (1, TIMESTAMP '2024-01-01 10:05:00', false, true), | |
| (2, TIMESTAMP '2024-01-01 10:00:00', true, false), | |
| (2, TIMESTAMP '2024-01-01 10:01:00', true, true) | |
| ) AS t(user_id, ts, c1, c2); | |
| SELECT | |
| user_id, | |
| sequence_match('(?1)(?2)', ts, c1, c2) AS matched, | |
| sequence_count('(?1)(?2)', ts, c1, c2) AS cnt | |
| FROM grouped | |
| GROUP BY user_id | |
| ORDER BY user_id; | |
| " | |
| - name: E2E - load test (100K events) | |
| run: | | |
| ./duckdb -unsigned -c " | |
| LOAD 'build/release/behavioral.duckdb_extension'; | |
| -- Generate 100K events for load testing | |
| CREATE TABLE load_test AS | |
| SELECT | |
| (i / 100)::INTEGER AS user_id, | |
| TIMESTAMP '2024-01-01' + (i * INTERVAL '1 second') AS ts, | |
| (i % 3 = 0) AS c1, | |
| (i % 5 = 0) AS c2, | |
| (i % 7 = 0) AS c3 | |
| FROM generate_series(1, 100000) AS t(i); | |
| -- Sessionize (window function) | |
| SELECT COUNT(*) AS session_count FROM ( | |
| SELECT DISTINCT sessionize(ts, INTERVAL '5 minutes') OVER (PARTITION BY user_id ORDER BY ts) AS sid | |
| FROM load_test | |
| ); | |
| -- Retention | |
| SELECT COUNT(*) FROM ( | |
| SELECT user_id, retention(c1, c2, c3) FROM load_test GROUP BY user_id | |
| ); | |
| -- Window funnel | |
| SELECT COUNT(*) FROM ( | |
| SELECT user_id, window_funnel(INTERVAL '1 hour', ts, c1, c2, c3) | |
| FROM load_test GROUP BY user_id | |
| ); | |
| -- Sequence match | |
| SELECT COUNT(*) FROM ( | |
| SELECT user_id, sequence_match('(?1).*(?2)', ts, c1, c2, c3) | |
| FROM load_test GROUP BY user_id | |
| ); | |
| -- Sequence count | |
| SELECT COUNT(*) FROM ( | |
| SELECT user_id, sequence_count('(?1)(?2)', ts, c1, c2, c3) | |
| FROM load_test GROUP BY user_id | |
| ); | |
| SELECT 'Load test passed: 100K events, all 5 aggregate functions' AS result; | |
| " | |
| - name: E2E - no match cases | |
| run: | | |
| ./duckdb -unsigned -c " | |
| LOAD 'build/release/behavioral.duckdb_extension'; | |
| -- Empty input | |
| CREATE TABLE empty_events(ts TIMESTAMP, c1 BOOLEAN, c2 BOOLEAN); | |
| SELECT sequence_match('(?1)(?2)', ts, c1, c2) FROM empty_events; | |
| SELECT sequence_count('(?1)(?2)', ts, c1, c2) FROM empty_events; | |
| SELECT window_funnel(INTERVAL '1 hour', ts, c1, c2) FROM empty_events; | |
| -- No match | |
| CREATE TABLE nomatch AS SELECT * FROM (VALUES | |
| (TIMESTAMP '2024-01-01 10:00:00', true, false), | |
| (TIMESTAMP '2024-01-01 10:05:00', true, false) | |
| ) AS t(ts, c1, c2); | |
| SELECT | |
| sequence_match('(?1)(?2)', ts, c1, c2) AS matched, | |
| window_funnel(INTERVAL '1 hour', ts, c1, c2) AS funnel | |
| FROM nomatch; | |
| SELECT 'No-match cases passed' AS result; | |
| " | |
| - name: Platform summary | |
| if: always() | |
| run: | | |
| { | |
| echo "### E2E Results (${{ matrix.platform }})" | |
| echo "" | |
| echo "| Test | Status |" | |
| echo "|------|--------|" | |
| echo "| Extension build | ${{ steps.*.outcome == 'success' && '✅' || '⚠️ Check logs' }} |" | |
| echo "| Extension loads | Verified |" | |
| echo "| sessionize | Verified |" | |
| echo "| retention | Verified |" | |
| echo "| window_funnel | Verified |" | |
| echo "| window_funnel modes | Verified |" | |
| echo "| sequence_match/count | Verified |" | |
| echo "| sequence_match_events | Verified |" | |
| echo "| sequence_next_node | Verified |" | |
| echo "| GROUP BY aggregation | Verified |" | |
| echo "| 100K load test | Verified |" | |
| echo "| No-match cases | Verified |" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Upload E2E logs | |
| if: always() | |
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 | |
| with: | |
| name: e2e-logs-${{ matrix.platform }} | |
| path: | | |
| build/release/*.log | |
| build/release/behavioral.duckdb_extension | |
| retention-days: 7 | |
| if-no-files-found: ignore | |
| e2e-summary: | |
| name: E2E Summary | |
| needs: e2e-test | |
| if: always() | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Evaluate results | |
| run: | | |
| { | |
| echo "### E2E Test Summary" | |
| echo "" | |
| echo "| Platform | Result |" | |
| echo "|----------|--------|" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| RESULT="${{ needs.e2e-test.result }}" | |
| case "$RESULT" in | |
| success) | |
| echo "| All platforms | ✅ passed |" >> "$GITHUB_STEP_SUMMARY" | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| echo "✅ **All E2E tests passed.** Extension loads, all 7 functions execute correctly, 100K load test succeeded." >> "$GITHUB_STEP_SUMMARY" | |
| ;; | |
| *) | |
| echo "| One or more platforms | ❌ $RESULT |" >> "$GITHUB_STEP_SUMMARY" | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| echo "❌ **E2E tests failed.** Check individual platform logs above." >> "$GITHUB_STEP_SUMMARY" | |
| exit 1 | |
| ;; | |
| esac |