Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ on:
push:
branches:
- main
- rust-next
pull_request:
branches:
- main
- rust-next
types: [opened, synchronize, reopened, labeled]
env:
E2E: "1"
Expand All @@ -31,6 +33,7 @@ jobs:
apisix:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
version:
- 3.2.2
Expand Down Expand Up @@ -79,10 +82,22 @@ jobs:
# Run E2E tests
- name: Run E2E tests
run: npx nx run backend-apisix:test

# Run the Rust port's E2E tests against the same live apisix instance
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: rust -> target
- name: Run Rust E2E tests
working-directory: ./rust
run: |
rustup update stable
rustup default stable
cargo test -p adc-backend-apisix -- --ignored --test-threads=1
apisix-standalone:
runs-on: ubuntu-latest
if: contains(github.event.pull_request.labels.*.name, 'test/apisix-standalone') || github.event_name == 'push'
strategy:
fail-fast: false
matrix:
version:
- 3.13.0
Expand Down Expand Up @@ -120,6 +135,28 @@ jobs:
# Run E2E tests
- name: Run E2E tests
run: npx nx run backend-apisix-standalone:test

# Run the Rust port's E2E tests against the same live cluster. Each
# test function restarts every instance itself before it starts
# (see adc-backend-apisix-standalone's tests/common/mod.rs), so
# running after the TS suite here is safe — nothing from the TS
# suite's own state survives into the Rust tests.
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: rust -> target
- name: Run Rust E2E tests
working-directory: ./rust
run: |
rustup update stable
rustup default stable
cargo test -p adc-backend-apisix-standalone -- --ignored --test-threads=1
# Only useful when the step above fails: a bare 404 from an admin API
# request gives no clue why on its own — the container's own error
# log does.
- name: Dump APISIX standalone container logs
if: failure()
working-directory: ./libs/backend-apisix-standalone/e2e/assets
run: docker compose logs --no-color
api7:
runs-on: ubuntu-latest
if: contains(github.event.pull_request.labels.*.name, 'test/api7') || github.event_name == 'push'
Expand Down Expand Up @@ -174,3 +211,91 @@ jobs:
# Run API7 E2E tests
- name: Run E2E tests
run: npx nx run backend-api7:test
api7-rust:
runs-on: ubuntu-latest
if: contains(github.event.pull_request.labels.*.name, 'test/api7') || github.event_name == 'push'
permissions:
contents: read
packages: read
strategy:
fail-fast: false
matrix:
version: [3.5.5, 3.6.1, 3.7.8, 3.8.23, 3.9.14, 3.10.1, dev]
steps:
- name: Determine API7 image and license
run: |
if [ "${{ matrix.version }}" = "dev" ]; then
echo "BACKEND_API7_VERSION=999.999.999" >> $GITHUB_ENV
echo "API7_DASHBOARD_IMAGE=ghcr.io/api7/api7-ee-3-integrated" >> $GITHUB_ENV
echo "API7_IMAGE_TAG=dev" >> $GITHUB_ENV
{
echo "BACKEND_API7_LICENSE<<EOLICENSE"
echo "${{ secrets.BACKEND_API7_DEV_LICENSE }}"
echo "EOLICENSE"
} >> $GITHUB_ENV
else
echo "BACKEND_API7_VERSION=${{ matrix.version }}" >> $GITHUB_ENV
echo "API7_DASHBOARD_IMAGE=api7/api7-ee-3-integrated" >> $GITHUB_ENV
echo "API7_IMAGE_TAG=v${{ matrix.version }}" >> $GITHUB_ENV
{
echo "BACKEND_API7_LICENSE<<EOLICENSE"
echo "${{ secrets.BACKEND_API7_LICENSE }}"
echo "EOLICENSE"
} >> $GITHUB_ENV
fi

- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- name: Login to GHCR
if: matrix.version == 'dev'
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# Runs on its own dashboard instance, independent of the `api7` job's
# (rather than reusing that job's live instance after the TS suite runs)
# — the Rust e2e suite's `common::client()` performs its own admin
# login / password rotation / license activation / token minting on
# first use (see adc-backend-api7's tests/common/mod.rs), which would
# collide with the TS suite doing the same dance against a shared
# instance.
- name: Setup API7 Instance via Docker Compose
working-directory: ./libs/backend-api7/e2e/assets
run: |
if [ "${{ matrix.version }}" = "dev" ]; then docker compose pull; fi
docker compose up -d

# Run the Rust port's E2E tests
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: rust -> target
# Each test file below is its own process with no state shared
# between them, but they all talk to the one dashboard instance
# started above — so the admin login / password rotation / license
# activation / token minting dance runs here exactly once, and its
# result is shared with every other test file as `TOKEN` (see
# tests/e2e_init.rs), instead of each independently repeating it
# against a dashboard the first one to run already mutated.
- name: Bootstrap a shared API7 token
working-directory: ./rust
run: |
rustup update stable
rustup default stable
cargo test -p adc-backend-api7 --test e2e_init -- --ignored
- name: Run Rust E2E tests
working-directory: ./rust
run: |
rustup update stable
rustup default stable
cargo test -p adc-backend-api7 -- --ignored --test-threads=1
# Only useful when the step above fails: `BackendError::Api`'s
# message is empty for a 500 with no response body, which the
# dashboard's own logs can actually explain.
- name: Dump API7 dashboard logs
if: failure()
working-directory: ./libs/backend-api7/e2e/assets
run: docker compose logs --no-color
62 changes: 62 additions & 0 deletions .github/workflows/unit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ on:
push:
branches:
- main
- rust-next
pull_request:
branches:
- main
- rust-next
jobs:
test:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -35,3 +37,63 @@ jobs:
run: npx nx run backend-api7:test
- name: Run OpenAPI Converter unit tests
run: npx nx run converter-openapi:test
rust:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Setup Rust toolchain
run: |
rustup update stable
rustup default stable
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: rust -> target

- name: Build
working-directory: ./rust
run: cargo build --workspace
- name: Clippy
working-directory: ./rust
run: cargo clippy --workspace --all-targets -- -D warnings
- name: Run unit tests
working-directory: ./rust
run: cargo test --workspace
rust-build:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
artifact: adc-linux-x64
- os: macos-latest
target: aarch64-apple-darwin
artifact: adc-macos-arm64
runs-on: ${{ matrix.os }}
permissions:
contents: read
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Setup Rust toolchain
run: |
rustup update stable
rustup default stable
rustup target add ${{ matrix.target }}
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
workspaces: rust -> target
- name: Build adc-cli
working-directory: ./rust
run: cargo build --release -p adc-cli --target ${{ matrix.target }}
- name: Stage binary
run: cp rust/target/${{ matrix.target }}/release/adc adc-${{ matrix.target }}
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ matrix.artifact }}
path: adc-${{ matrix.target }}
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,6 @@ vitest.config.*.timestamp*

.nx/polygraph
.nx/self-healing
.nx/migrate-runs
.nx/migrate-runs

rust/target/
5 changes: 5 additions & 0 deletions fixtures/differ/basic.adapts_to_default_core_values.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"local": { "consumers": [{ "username": "alice", "plugins": {} }] },
"remote": { "consumers": [{ "username": "alice", "description": "", "plugins": {} }] },
"defaultValue": { "core": { "consumer": { "description": "" } }, "plugins": {} }
}
5 changes: 5 additions & 0 deletions fixtures/differ/basic.adapts_to_default_plugin_values.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"local": { "consumers": [{ "username": "alice", "plugins": { "key-auth": { "key": "key" } } }] },
"remote": { "consumers": [{ "username": "alice", "plugins": { "key-auth": { "key": "key", "added": "added" } } }] },
"defaultValue": { "core": {}, "plugins": { "key-auth": { "added": "added" } } }
}
12 changes: 12 additions & 0 deletions fixtures/differ/basic.boolean_defaults_merged_correctly.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"local": { "services": [{ "name": "HTTP", "path_prefix": "/test", "strip_path_prefix": false }] },
"remote": {
"services": [{
"id": "f40b27d6b8b9a4fc3827264c1da16a04ecf4d094",
"name": "HTTP",
"path_prefix": "/test",
"strip_path_prefix": true
}]
},
"defaultValue": { "core": { "service": { "strip_path_prefix": true } }, "plugins": {} }
}
4 changes: 4 additions & 0 deletions fixtures/differ/basic.create_resource.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"local": { "consumers": [{ "username": "alice", "plugins": {} }] },
"remote": {}
}
4 changes: 4 additions & 0 deletions fixtures/differ/basic.delete_resource.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"local": {},
"remote": { "consumers": [{ "username": "alice", "plugins": {} }] }
}
4 changes: 4 additions & 0 deletions fixtures/differ/basic.empty_input_yields_empty_output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"local": {},
"remote": {}
}
8 changes: 8 additions & 0 deletions fixtures/differ/basic.generates_hashed_resource_id.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"local": {
"ssls": [
{ "snis": ["demo-sni1", "demo-sni2"], "certificates": [{ "certificate": "cert", "key": "key" }] }
]
},
"remote": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"local": {
"services": [{
"name": "Test Service",
"path_prefix": "/test",
"plugins": { "test": { "testKey": "testValue" } }
}]
},
"remote": {
"services": [{
"id": "5139f1c19641e214d3b9c57949d1578c2482b940",
"name": "Test Service",
"plugins": { "test": { "testKey": "testValue", "added": "added" } }
}]
},
"defaultValue": { "core": {}, "plugins": { "test": { "added": "added" } } }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"local": {
"services": [{
"name": "Test Service",
"upstream": { "nodes": [{ "host": "0.0.0.0", "port": 443, "weight": 1 }] }
}]
},
"remote": {
"services": [{
"id": "5139f1c19641e214d3b9c57949d1578c2482b940",
"name": "Test Service",
"upstream": { "nodes": [{ "host": "0.0.0.0", "port": 443, "weight": 1, "priority": 0 }] }
}]
},
"defaultValue": {
"core": { "service": { "upstream": { "nodes": [{ "priority": 0 }] } } },
"plugins": {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"local": {
"services": [
{ "name": "HTTP", "routes": [{ "name": "HTTP 1", "uris": ["/1"] }] },
{ "name": "Stream", "stream_routes": [{ "name": "Stream 1", "server_port": 5432 }] }
]
},
"remote": {
"services": [
{
"id": "f40b27d6b8b9a4fc3827264c1da16a04ecf4d094",
"name": "HTTP",
"routes": [{ "id": "f9e217f5cef4955ffce8d8da8b8e17918f85deb7", "name": "HTTP 1", "uris": ["/1"] }]
},
{
"id": "df063869e11d7a9aa132cd4a984f7b5eb870d656",
"name": "Stream",
"stream_routes": [{ "id": "05a5e25f718f3fd8d9d3b02e39bdf6e8ab274220", "name": "Stream 1", "server_port": 5432 }]
}
]
},
"defaultValue": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"local": { "services": [{ "name": "Test Service", "test1": {} }] },
"remote": {
"services": [{
"id": "5139f1c19641e214d3b9c57949d1578c2482b940",
"name": "Test Service",
"test": "test",
"test1": { "test2": "test2" }
}]
},
"defaultValue": {
"core": {
"service": { "test": "test", "test1": { "test2": "test2", "test3": { "test4": "test4" } } }
},
"plugins": {}
}
}
14 changes: 14 additions & 0 deletions fixtures/differ/basic.sorted_by_event_type.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"local": {
"consumers": [
{ "username": "createConsumer", "plugins": {} },
{ "username": "updatedConsumer", "plugins": { "key-auth": {} } }
]
},
"remote": {
"consumers": [
{ "username": "updatedConsumer", "plugins": {} },
{ "username": "deletedConsumer", "plugins": {} }
]
}
}
4 changes: 4 additions & 0 deletions fixtures/differ/basic.update_resource.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"local": { "consumers": [{ "username": "alice", "plugins": { "key-auth": { "key": "alice-key" } } }] },
"remote": { "consumers": [{ "username": "alice", "plugins": {} }] }
Comment on lines +2 to +3

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the two update fixtures behaviorally distinct.

Both files contain the same local and remote payload, so the suite cannot distinguish a generic resource update from plugin addition.

  • fixtures/differ/basic.update_resource.json#L2-L3: change the payload to a distinct resource update, or remove this duplicate fixture.
  • fixtures/differ/basic.update_resource_add_plugin.json#L2-L3: retain this payload only for the add-key-auth-plugin scenario.
📍 Affects 2 files
  • fixtures/differ/basic.update_resource.json#L2-L3 (this comment)
  • fixtures/differ/basic.update_resource_add_plugin.json#L2-L3
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fixtures/differ/basic.update_resource.json` around lines 2 - 3, Make the
update fixtures behaviorally distinct: in
fixtures/differ/basic.update_resource.json lines 2-3, replace the duplicate
plugin-addition payload with a distinct generic resource update or remove the
fixture; in fixtures/differ/basic.update_resource_add_plugin.json lines 2-3,
retain the existing payload for the add-key-auth-plugin scenario.

}
Loading
Loading