Files
langflow/.github/workflows/ci.yml
Yuqi Tang 3d7ad8b088 feat: Adding backend unit tests (#9416)
* Add comprehensive unit tests for Celery modules (celery_app.py and celeryconfig.py)

- Add test_celery_app.py with 10 unit tests covering make_celery function
- Add test_celeryconfig.py with 14 unit tests covering configuration validation
- All tests use proper mocking to avoid external dependencies
- Achieve 100% test pass rate (24/24 tests)
- Improve test coverage from 0% to meaningful levels for core Celery functionality
- Fix linting issues: combine nested with statements and optimize startswith calls

* fix: Resolve linting issues in utility test files

- Remove unused variables and function arguments
- Fix boolean positional arguments in test calls
- Combine nested with statements
- Fix string literals in exception messages
- Add missing newlines at end of files

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Add comprehensive unit tests for backend modules

This commit adds focused unit test coverage for critical backend components
with meaningful test implementations:

API Layer:
- AsyncStreamingLLMCallbackHandleSIO callback handler tests
- WebSocket streaming, agent actions, and error handling

Components:
- NotifyComponent initialization and configuration tests
- PythonCodeStructuredTool component structure and build tests

Services:
- SocketIOService session management and event handling tests
- S3StorageService AWS operations and error handling tests

Utils:
- async_helpers: AsyncLock, async utilities, and error handling
- component_utils: Component operations and data processing
- compression: Response compression and encoding utilities
- concurrency: Thread management and parallel processing
- lazy_load: Module lazy loading and import optimization
- payload: Data transformation and API payload handling
- schemas: Data validation and schema verification
- version: Version parsing and compatibility checking
- voice_utils: Audio processing and voice feature utilities

All tests include comprehensive mocking, error scenarios, edge cases,
and integration patterns following pytest best practices. Removed
placeholder functions and kept only meaningful test implementations.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix linting issues in test_data_structure.py and test_util.py

- Fix boolean positional value warnings with noqa comments
- Replace string literals in exceptions with variables
- Combine nested with statements
- Remove unused variables and function arguments
- Use proper parameter names with underscores for unused args

All ruff checks now pass for these test files.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* merge conflicts

* Add comprehensive unit tests for logic components

- Added test_conditional_router.py with 51 tests for ConditionalRouterComponent
- Added test_data_conditional_router.py with 53 tests for DataConditionalRouterComponent
- Added test_listen.py with 22 tests for ListenComponent
- Added test_notify_component.py with 12 tests for NotifyComponent
- Added test_pass_message.py with 16 tests for PassMessageComponent
- Added test_flow_tool.py with 17 tests for FlowToolComponent

Total: 171 comprehensive tests covering initialization, functionality, edge cases, and error handling
All tests follow clean code practices and achieve high coverage

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* merge conflict

* merge conflict

* fix import errors in unit tests

* fix ruff errors

* [autofix.ci] apply automated fixes

* update path for test_graph in smoke tests

* fix smoke test and failing tests

* modify smoke tests paths

* run lfx tests separately

* fix test_conditional_router.py

* [autofix.ci] apply automated fixes

* fix FlowToolComponent tests

* [autofix.ci] apply automated fixes

* fix: Ensure CLI error output is visible in tests

- Fix error messages not appearing in test output when stdout is redirected
- Temporarily restore stdout before writing error JSON to ensure visibility
- Fix overly broad test condition that matched 'langflow' in file paths
- Only check for actual langflow/lfx import errors, not dependency errors

Fixes 3 failing tests in test_run_starter_projects.py:
- News Aggregator.json
- Pokédex Agent.json
- Price Deal Finder.json

All 736 LFX tests now pass.

* [autofix.ci] apply automated fixes

* Revert "fix: Ensure CLI error output is visible in tests"

This reverts commit 390c669043.

* fix: Correct import paths in test_concurrency.py

* fix test_voice_utils

* fix test_util

* [autofix.ci] apply automated fixes

* fiix test_util

* [autofix.ci] apply automated fixes

* fix test_voice_util.py

* unused args:

* refactor tests to use componentclient

* [autofix.ci] apply automated fixes

* fix ruff errors in test_flow_tool

* skip if api key is not present

* make workflow more robust

* fix mocking in test_celery_app

* [autofix.ci] apply automated fixes

* add celery

* add removed files back

* include once functional

* skip celery tests

* review comments addressed

* remove celery from dependency

* change env var validation

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: himavarshagoutham <himavarshajan17@gmail.com>
Co-authored-by: Himavarsha <40851462+HimavarshaVS@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-10-16 16:46:26 +00:00

460 lines
18 KiB
YAML

name: CI
on:
workflow_call:
inputs:
ref:
description: "(Optional) Ref to checkout"
required: false
type: string
python-versions:
description: "Python Versions"
required: false
type: string
default: "['3.10']"
frontend-tests-folder:
description: "Frontend Tests Folder"
required: false
type: string
default: "tests/core"
release:
description: "Release"
required: false
type: boolean
default: false
run-all-tests:
description: "Run all tests regardless of file changes (skips path filtering)"
required: false
type: boolean
default: false
runs-on:
description: "Runner to use for the tests"
required: false
type: string
default: "ubuntu-latest"
workflow_dispatch:
inputs:
ref:
description: "(Optional) Ref to checkout"
required: false
type: string
openai_api_key:
description: "OpenAI API Key"
required: false
type: string
store_api_key:
description: "Store API Key"
required: false
type: string
python-versions:
description: "Python Versions"
required: false
type: string
default: "['3.10']"
runs-on:
description: "Runner to use for the tests"
required: false
type: choice
options:
- ubuntu-latest
- self-hosted
- "[self-hosted, linux, ARM64, langflow-ai-arm64-40gb]"
default: ubuntu-latest
pull_request:
types: [opened, synchronize, labeled]
merge_group:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
STORE_API_KEY: ${{ secrets.STORE_API_KEY }}
TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }}
jobs:
echo-inputs:
name: Echo Inputs
runs-on: ubuntu-latest
steps:
- name: Echo inputs
run: |
echo "Inputs:"
echo " ref: ${{ inputs.ref }}"
echo " python-versions: ${{ inputs.python-versions }}"
echo " frontend-tests-folder: ${{ inputs.frontend-tests-folder }}"
echo " release: ${{ inputs.release }}"
echo " run-all-tests: ${{ inputs.run-all-tests }}"
echo " runs-on: ${{ inputs.runs-on }}"
check-nightly-status:
name: Check PyPI Version Update
runs-on: ubuntu-latest
outputs:
should-proceed: ${{ steps.check-pypi.outputs.success }}
steps:
- name: Check PyPI package update
id: check-pypi
run: |
# Get today's date in ISO format for comparison
TODAY=$(date -u +"%Y-%m-%d")
echo "Today's date: $TODAY"
# Query PyPI API for the langflow package
HTTP_STATUS=$(curl -s -o response.json -w "%{http_code}" https://pypi.org/pypi/langflow-nightly/json)
# Check HTTP status code first
if [ "$HTTP_STATUS" -ne 200 ]; then
echo "Error: PyPI API returned HTTP status $HTTP_STATUS"
echo "success=false" >> $GITHUB_OUTPUT
exit 0
fi
# Check if response is valid JSON before proceeding
if ! jq -e . response.json >/dev/null 2>&1; then
echo "Error: Invalid JSON response from PyPI API"
echo "Response preview:"
head -n 10 response.json
echo "success=false" >> $GITHUB_OUTPUT
exit 0
fi
# Extract the latest version
LATEST_VERSION=$(jq -r '.info.version // empty' response.json)
if [ -z "$LATEST_VERSION" ]; then
echo "Could not extract latest version"
echo "success=false" >> $GITHUB_OUTPUT
exit 0
fi
# Extract the release date of the latest version
RELEASE_DATE=$(jq -r --arg ver "$LATEST_VERSION" '.releases[$ver][0].upload_time_iso_8601 // empty' response.json | cut -d'T' -f1)
if [ -z "$RELEASE_DATE" ]; then
echo "Could not extract release date"
echo "success=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "Latest version: $LATEST_VERSION"
echo "Release date: $RELEASE_DATE"
# Check if the release date is today
if [[ "$RELEASE_DATE" == "$TODAY" ]]; then
echo "Package was updated today"
echo "success=true" >> $GITHUB_OUTPUT
else
echo "Package was not updated today"
echo "success=false" >> $GITHUB_OUTPUT
fi
# Clean up
rm -f response.json
set-ci-condition:
name: Should Run CI
runs-on: ubuntu-latest
outputs:
should-run-ci: ${{ github.event.pull_request.draft == false || (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call' || github.event_name == 'merge_group') }}
should-run-tests: ${{ !contains(github.event.pull_request.labels.*.name, 'fast-track') || github.event_name == 'workflow_call' || github.event_name == 'workflow_dispatch' || github.event_name == 'merge_group' }}
steps:
# Do anything just to make the job run
- run: echo "Debug CI Condition"
- run: echo "Labels -> ${{ join(github.event.pull_request.labels.*.name, ',') }}"
- run: echo "IsDraft -> ${{ github.event.pull_request.draft }}"
- run: echo "Event name -> ${{ github.event_name }}"
- run: echo "Should run ci -> ${{ (github.event.pull_request.draft == false) || (github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_call' || github.event_name == 'merge_group') }}"
- run: echo "Should run tests -> ${{ !contains(github.event.pull_request.labels.*.name, 'fast-track') || github.event_name == 'workflow_call' || github.event_name == 'workflow_dispatch' || github.event_name == 'merge_group' }}"
path-filter:
needs: set-ci-condition
if: ${{ needs.set-ci-condition.outputs.should-run-ci == 'true' && !inputs.run-all-tests }}
name: Filter Paths
runs-on: ubuntu-latest
outputs:
python: ${{ steps.filter.outputs.python }}
frontend: ${{ steps.filter.outputs.frontend }}
docs: ${{ steps.filter.outputs.docs }}
frontend-tests: ${{ steps.filter.outputs.frontend-tests }}
components-changes: ${{ steps.filter.outputs.components-changes }}
starter-projects-changes: ${{ steps.filter.outputs.starter-projects-changes }}
starter-projects: ${{ steps.filter.outputs.starter-projects }}
components: ${{ steps.filter.outputs.components }}
workspace: ${{ steps.filter.outputs.workspace }}
api: ${{ steps.filter.outputs.api }}
database: ${{ steps.filter.outputs.database }}
docker: ${{ steps.filter.outputs.docker }}
docs-only: ${{
steps.filter.outputs.docs == 'true' &&
steps.filter.outputs.python != 'true' &&
steps.filter.outputs.frontend != 'true' &&
steps.filter.outputs['frontend-tests'] != 'true' &&
steps.filter.outputs['components-changes'] != 'true' &&
steps.filter.outputs['starter-projects-changes'] != 'true' &&
steps.filter.outputs['starter-projects'] != 'true' &&
steps.filter.outputs.components != 'true' &&
steps.filter.outputs.workspace != 'true' &&
steps.filter.outputs.api != 'true' &&
steps.filter.outputs.database != 'true' &&
steps.filter.outputs.docker != 'true'
}}
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
ref: ${{ inputs.ref || github.ref }}
- name: Filter Paths
id: filter
uses: dorny/paths-filter@v3
with:
filters: ./.github/changes-filter.yaml
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install deps for coverage check
run: |
python -m pip install --upgrade pip
pip install --disable-pip-version-check --no-cache-dir pyyaml
- name: Validate Filter Coverage
continue-on-error: true
shell: bash
run: |
BASE_SHA="${{ github.event.pull_request.base.sha }}"
BASE_REF="${{ github.event.pull_request.base.ref || 'main' }}"
# Ensure base commit is present locally
git fetch --no-tags --depth=1 origin "$BASE_REF" || true
git fetch --no-tags --depth=1 origin "$BASE_SHA" || true
git diff --name-only "${BASE_SHA:-origin/$BASE_REF}"...HEAD | python scripts/check_changes_filter.py
test-backend:
needs: [path-filter, set-ci-condition]
name: Run Backend Tests
if: |
always() &&
!cancelled() &&
needs.set-ci-condition.outputs.should-run-tests == 'true' &&
(inputs.run-all-tests || needs.path-filter.outputs.python == 'true')
uses: ./.github/workflows/python_test.yml
with:
python-versions: ${{ inputs.python-versions || '["3.10"]' }}
runs-on: ${{ (inputs['runs-on'] && startsWith(format('{0}', inputs['runs-on']), '[') && fromJSON(inputs['runs-on'])) || inputs['runs-on'] || github.event.inputs['runs-on'] || 'ubuntu-latest' }}
ref: ${{ inputs.ref || github.ref }}
secrets:
OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}"
ANTHROPIC_API_KEY: "${{ secrets.ANTHROPIC_API_KEY }}"
CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}"
test-frontend-unit:
needs: [path-filter, set-ci-condition]
name: Run Frontend Unit Tests
if: |
always() &&
!cancelled() &&
needs.set-ci-condition.outputs.should-run-tests == 'true' &&
(inputs.run-all-tests || needs.path-filter.outputs.frontend == 'true')
uses: ./.github/workflows/jest_test.yml
with:
ref: ${{ inputs.ref || github.ref }}
secrets:
CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}"
test-frontend:
needs: [path-filter, set-ci-condition]
name: Run Frontend Tests
if: |
always() &&
!cancelled() &&
needs.set-ci-condition.outputs.should-run-tests == 'true' &&
(inputs.run-all-tests || needs.path-filter.outputs.frontend == 'true' || needs.path-filter.outputs.frontend-tests == 'true' || needs.path-filter.outputs.components-changes == 'true' || needs.path-filter.outputs.starter-projects-changes == 'true' || needs.path-filter.outputs.starter-projects == 'true' || needs.path-filter.outputs.components == 'true' || needs.path-filter.outputs.workspace == 'true' || needs.path-filter.outputs.api == 'true' || needs.path-filter.outputs.database == 'true')
uses: ./.github/workflows/typescript_test.yml
with:
tests_folder: ${{ inputs.frontend-tests-folder }}
release: ${{ inputs.release || false }}
runs-on: ${{ (inputs['runs-on'] && startsWith(format('{0}', inputs['runs-on']), '[') && fromJSON(inputs['runs-on'])) || inputs['runs-on'] || github.event.inputs['runs-on'] || 'ubuntu-latest' }}
ref: ${{ inputs.ref || github.ref }}
secrets:
OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}"
STORE_API_KEY: "${{ secrets.STORE_API_KEY }}"
ANTHROPIC_API_KEY: "${{ secrets.ANTHROPIC_API_KEY }}"
TAVILY_API_KEY: "${{ secrets.TAVILY_API_KEY }}"
lint-backend:
needs: path-filter
if: |
always() &&
!cancelled() &&
(inputs.run-all-tests || needs.path-filter.outputs.python == 'true')
name: Lint Backend
uses: ./.github/workflows/lint-py.yml
test-docs-build:
needs: path-filter
if: |
always() &&
!cancelled() &&
(inputs.run-all-tests || needs.path-filter.outputs.docs == 'true')
name: Test Docs Build
uses: ./.github/workflows/docs_test.yml
test-templates:
needs: [path-filter, set-ci-condition]
name: Test Starter Templates
if: |
always() &&
!cancelled() &&
needs.set-ci-condition.outputs.should-run-tests == 'true' &&
(inputs.run-all-tests || needs.path-filter.outputs.python == 'true' || needs.path-filter.outputs.frontend == 'true')
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
ref: ${{ inputs.ref || github.ref }}
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: 3.12
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
version: "latest"
- name: Install dependencies
run: |
uv sync --dev
- name: Test all starter project templates
run: |
uv run pytest src/backend/tests/unit/template/test_starter_projects.py -v -n auto
test-docker:
needs: [path-filter, set-ci-condition]
name: Test Docker Images
if: ${{ needs.path-filter.outputs.docker == 'true' && needs.set-ci-condition.outputs.should-run-tests == 'true' }}
uses: ./.github/workflows/docker_test.yml
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
# https://github.com/langchain-ai/langchain/blob/master/.github/workflows/check_diffs.yml
ci_success:
name: "CI Success"
needs:
[
test-backend,
test-frontend-unit,
test-frontend,
lint-backend,
test-docs-build,
test-templates,
test-docker,
set-ci-condition,
path-filter,
check-nightly-status,
]
if: ${{ always() }}
runs-on: ubuntu-latest
env:
JOBS_JSON: ${{ toJSON(needs) }}
RESULTS_JSON: ${{ toJSON(needs.*.result) }}
# Skip nightly build check if only docs files changed
DOCS_ONLY: ${{ needs.path-filter.outputs.docs-only }}
EXIT_CODE: ${{ (contains(needs.*.result, 'failure') ||
contains(needs.*.result, 'cancelled') ||
(needs.check-nightly-status.outputs.should-proceed != 'true' && github.event_name != 'workflow_dispatch' && needs.path-filter.outputs.docs-only != 'true'))
&& '1' || '0' }}
steps:
- name: "CI Success"
run: |
echo "=== CI Status Summary ==="
echo "Should run tests: ${{ needs.set-ci-condition.outputs.should-run-tests }}"
echo "Should run CI: ${{ needs.set-ci-condition.outputs.should-run-ci }}"
echo "Nightly build status: ${{ needs.check-nightly-status.outputs.should-proceed }}"
echo "Event type: ${{ github.event_name }}"
echo "Docs only changes: $DOCS_ONLY"
echo "Python changes: ${{ needs.path-filter.outputs.python }}"
echo "Frontend changes: ${{ needs.path-filter.outputs.frontend }}"
echo "Docs changes: ${{ needs.path-filter.outputs.docs }}"
echo ""
# Check for job failures
if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then
echo "❌ CI FAILED: One or more jobs failed"
echo ""
echo "Failed jobs:"
# Dynamically list failed jobs with helpful descriptions
echo "$JOBS_JSON" | jq -r '
to_entries[]
| select(.value.result=="failure")
| .key as $job
| if $job == "test-backend" then
" - Backend Tests: Check Python code, tests, and dependencies"
elif $job == "test-frontend-unit" then
" - Frontend Unit Tests: Check React components and unit test logic"
elif $job == "test-frontend" then
" - Frontend E2E Tests: Check integration tests and UI functionality"
elif $job == "lint-backend" then
" - Backend Linting: Run '\''make format_backend'\'' then '\''make lint'\'' to fix code style issues"
elif $job == "test-docs-build" then
" - Documentation Build: Check documentation syntax and build process"
elif $job == "test-templates" then
" - Template Tests: Check starter project templates"
elif $job == "test-docker" then
" - Docker Tests: Check Docker image builds and version verification"
elif $job == "path-filter" then
" - Path Filter: File path filtering failed"
elif $job == "set-ci-condition" then
" - CI Condition Check: CI condition evaluation failed"
elif $job == "check-nightly-status" then
" - Nightly Status Check: PyPI package status check failed"
else
" - \($job): See job log for details"
end
'
echo ""
echo "🔧 Next steps:"
echo " 1. Review the failed job logs above"
echo " 2. Fix the identified issues in your code"
echo " 3. Push your changes to re-run the tests"
elif [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
echo "⚠️ CI CANCELLED: One or more jobs were cancelled"
echo ""
echo "🔧 Next steps:"
echo " 1. Check if the cancellation was intentional"
echo " 2. Re-run the workflow if needed"
elif [[ "${{ needs.check-nightly-status.outputs.should-proceed }}" != "true" && "${{ github.event_name }}" != "workflow_dispatch" && "$DOCS_ONLY" != "true" ]]; then
echo "🚫 CI BLOCKED: Nightly build is broken"
echo ""
echo "The nightly PyPI package was not updated today, indicating the nightly build failed."
echo ""
echo "🔧 Next steps:"
echo " 1. Work with the team to investigate and fix the nightly build"
echo " 2. Check the nightly build logs for errors"
echo " 3. Once the nightly build is fixed and publishes successfully, re-run this workflow"
echo " 4. Alternatively, use 'workflow_dispatch' to manually override this check if needed"
echo " 5. Note: PRs with only documentation changes can bypass this check"
else
echo "✅ CI SUCCESS: All checks passed!"
echo ""
echo "🎉 Your changes are ready:"
echo " - All tests passed"
echo " - Code quality checks passed"
echo " - Nightly build is healthy"
fi
echo ""
echo "Exit code: $EXIT_CODE"
exit $EXIT_CODE