katzenpost_thin_client 0.0.23

This rust crate provides an async thin client library for Katzenpost, a post quantum decryption mixnet.
Documentation
name: Integration Tests with Docker Mixnet

on:
  pull_request:
    branches: [ main, master, update_python_pigeonhole ]

jobs:
  test-integration-docker:
    permissions:
      contents: read
      packages: write
    runs-on: ubuntu-latest
    timeout-minutes: 120

    steps:
      - name: Checkout thinclient repository
        uses: actions/checkout@v4
        with:
          path: thinclient

      - name: Checkout katzenpost repository
        uses: actions/checkout@v4
        with:
          repository: katzenpost/katzenpost
          ref: d5a6349a34c4a2d4bca587f4be86ce92f2873b12 # v0.0.90: Pigeonhole docstring rewrites
          path: katzenpost

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Cache Docker layers
        uses: actions/cache@v4
        with:
          path: /tmp/.buildx-cache
          key: ${{ runner.os }}-buildx-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-buildx-

      - name: Configure podman socket
        run: |
          systemctl --user start podman.socket

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Set up Rust
        uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          toolchain: stable

      - name: Install thinclient dependencies
        run: |
          cd thinclient
          python -m pip install --upgrade pip
          pip install -e '.[test]'

      - name: Log in to GitHub Container Registry
        run: |
          echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
          echo "${{ secrets.GITHUB_TOKEN }}" | podman login ghcr.io -u "${{ github.actor }}" --password-stdin || true

      - name: Build and start the mixnet
        run: |
          cd katzenpost/docker && make start wait

      - name: Brief pause to ensure mixnet is fully ready
        run: sleep 30

      - name: Detect pigeonhole-cp-relevant changes
        id: smoke_filter
        uses: dorny/paths-filter@v3
        with:
          working-directory: thinclient
          filters: |
            smoke:
              - 'src/bin/**'
              - 'src/persistent/**'
              - 'src/pigeonhole.rs'
              - 'src/core.rs'
              - 'tests/smoke_pigeonhole_cp.rs'
              - 'Cargo.toml'
              - 'Cargo.lock'

      - name: Run Python and Rust integration tests in parallel
        id: parallel-tests
        timeout-minutes: 60
        run: |
          cd thinclient

          # Run Python tests in background.
          python -m pytest tests/ \
            -vvv -s --tb=short --timeout=1200 -n 3 \
            --durations=25 \
            > /tmp/python-test-output.txt 2>&1 &
          PYTHON_PID=$!

          # Run Rust tests in background
          cargo test --test '*' -- --nocapture --test-threads=3 \
            > /tmp/rust-test-output.txt 2>&1 &
          RUST_PID=$!

          # Wait for both and capture exit codes
          PYTHON_RC=0
          RUST_RC=0
          wait $PYTHON_PID || PYTHON_RC=$?
          wait $RUST_PID || RUST_RC=$?

          # Print outputs
          echo "=== Python test output ==="
          cat /tmp/python-test-output.txt
          echo ""
          echo "=== Rust test output ==="
          cat /tmp/rust-test-output.txt
          echo ""

          # Report results
          echo "Python tests exit code: $PYTHON_RC"
          echo "Rust tests exit code: $RUST_RC"

          # Fail if either failed
          if [ $PYTHON_RC -ne 0 ] || [ $RUST_RC -ne 0 ]; then
            exit 1
          fi

      - name: Run pigeonhole-cp smoke test
        if: steps.smoke_filter.outputs.smoke == 'true'
        timeout-minutes: 10
        run: |
          cd thinclient
          cargo test --features cli --test smoke_pigeonhole_cp -- --nocapture

      - name: Capture kpclientd container logs
        if: always()
        run: |
          if [ ! -d katzenpost/docker/voting_mixnet ]; then
            echo "voting_mixnet directory absent; the mixnet never started, nothing to capture"
            exit 0
          fi
          cd katzenpost/docker/voting_mixnet
          DOCKER_HOST="unix://$XDG_RUNTIME_DIR/podman/podman.sock" \
            podman compose logs --no-color --no-log-prefix kpclientd \
              > kpclientd.log 2>&1 || true

      - name: Stop the mixnet
        if: always()
        run: |
          cd katzenpost/docker && make stop

      - name: Upload testnet state
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: mixnet-${{ github.run_id }}-${{ github.job }}
          path: |
            katzenpost/docker/voting_mixnet/*/*.log
            katzenpost/docker/voting_mixnet/kpclientd.log
            katzenpost/docker/voting_mixnet/servicenode*/courier/*.log