Repository navigation
feat(behaviour): add isolated dev behaviour tests #1244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| name: Behaviour Tests | ||
|
|
||
| # Live behaviour tests use pull_request_target so authorized PRs can receive | ||
| # the dev-environment secrets. The gate runs on the base checkout before this | ||
| # job checks out and executes the PR head. | ||
|
|
||
| permissions: {} | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| paths: | ||
| - 'agents/**' | ||
|
ifireball marked this conversation as resolved.
Resolved
|
||
| - 'behaviour/**' | ||
| - 'config.yaml' | ||
| - 'env/**' | ||
| - 'harness/**' | ||
| - 'policies/**' | ||
| - 'schemas/**' | ||
| - 'scripts/**' | ||
|
qodo-code-review[bot] marked this conversation as resolved.
Resolved
|
||
| - 'go.mod' | ||
| - 'go.sum' | ||
| - 'Makefile' | ||
| - '.github/workflows/behaviour-tests.yml' | ||
| - '.github/actions/check-e2e-authorization/**' | ||
| - '.github/scripts/check-e2e-authorization.sh' | ||
| pull_request_target: | ||
|
ifireball marked this conversation as resolved.
Resolved
|
||
| types: [opened, synchronize, reopened, labeled] | ||
| merge_group: | ||
| workflow_dispatch: | ||
|
|
||
| concurrency: | ||
| group: >- | ||
| ${{ github.event_name == 'pull_request_target' | ||
| && format('behaviour-{0}', github.event.pull_request.number) | ||
| || format('{0}-{1}', github.workflow, github.ref) }} | ||
| cancel-in-progress: >- | ||
|
ifireball marked this conversation as resolved.
Resolved
|
||
| ${{ github.event_name == 'pull_request_target' | ||
| || github.ref != 'refs/heads/main' }} | ||
|
|
||
| jobs: | ||
| gate: | ||
| if: >- | ||
| github.event_name == 'pull_request_target' && | ||
| (github.event.action != 'labeled' || github.event.label.name == 'ok-to-test') | ||
| runs-on: ubuntu-24.04 | ||
| timeout-minutes: 5 | ||
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
| outputs: | ||
| authorized: ${{ steps.auth.outputs.authorized }} | ||
| steps: | ||
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | ||
| with: | ||
| ref: ${{ github.sha }} | ||
|
|
||
| - name: Check PR authorization | ||
| id: auth | ||
| uses: ./.github/actions/check-e2e-authorization | ||
| with: | ||
| pr_number: ${{ github.event.pull_request.number }} | ||
| repository: ${{ github.repository }} | ||
| pr_updated_at: ${{ github.event.pull_request.updated_at }} | ||
| event_action: ${{ github.event.action }} | ||
| pr_author_association: ${{ github.event.pull_request.author_association }} | ||
| pr_author_login: ${{ github.event.pull_request.user.login }} | ||
|
|
||
| behaviour: | ||
| needs: gate | ||
| if: >- | ||
|
ifireball marked this conversation as resolved.
Resolved
|
||
| !cancelled() && | ||
| ( | ||
| (github.event_name == 'push' && github.ref == 'refs/heads/main') || | ||
| github.event_name == 'merge_group' || | ||
| github.event_name == 'workflow_dispatch' || | ||
| (github.event_name == 'pull_request_target' && | ||
| needs.gate.outputs.authorized == 'true') | ||
| ) | ||
| environment: dev | ||
| runs-on: ubuntu-24.04 | ||
| timeout-minutes: 45 | ||
| permissions: | ||
| contents: read | ||
| id-token: write | ||
| env: | ||
| ENVIRONMENT: dev | ||
| BEHAVIOUR_SCM: github | ||
| BEHAVIOUR_CI: githubactions | ||
| BEHAVIOUR_INSTALL_MODE: per-repo | ||
| BEHAVIOUR_APP_SET: fullsend-test | ||
| BEHAVIOUR_SUITE_NAME: agents-bt-${{ github.run_id }} | ||
|
ifireball marked this conversation as resolved.
Resolved
|
||
| steps: | ||
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | ||
| with: | ||
| ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.sha }} | ||
| persist-credentials: false | ||
| allow-unsafe-pr-checkout: ${{ github.event_name == 'pull_request_target' }} | ||
|
ifireball marked this conversation as resolved.
Resolved
|
||
|
|
||
| - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 | ||
| with: | ||
| go-version-file: go.mod | ||
| cache-dependency-path: go.sum | ||
|
|
||
| - name: Authenticate to GCP | ||
| uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 | ||
| with: | ||
| workload_identity_provider: ${{ secrets.AGENTS_BT_E2E_GCP_WIF_PROVIDER }} | ||
| service_account: ${{ secrets.AGENTS_BT_E2E_GCP_SERVICE_ACCOUNT }} | ||
|
|
||
| - name: Resolve pinned fullsend source | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| shell: bash | ||
| run: | | ||
| version="$(go list -m -f '{{.Version}}' github.com/fullsend-ai/fullsend)" | ||
| if [[ "$version" =~ -0\.[0-9]{14}-([0-9a-f]+)$ ]]; then | ||
| ref="${BASH_REMATCH[1]}" | ||
| else | ||
| ref="$version" | ||
| fi | ||
| sha="$(gh api "repos/fullsend-ai/fullsend/commits/${ref}" --jq .sha)" | ||
| checkout="${RUNNER_TEMP}/fullsend" | ||
| git clone --filter=blob:none --no-checkout \ | ||
| https://github.com/fullsend-ai/fullsend.git "$checkout" | ||
| git -C "$checkout" \ | ||
| -c "http.extraheader=AUTHORIZATION: bearer ${GH_TOKEN}" \ | ||
| fetch --depth=1 origin "$sha" | ||
| git -C "$checkout" checkout --detach "$sha" | ||
| test "$(git -C "$checkout" rev-parse HEAD)" = "$sha" | ||
| grep -qx 'module github.com/fullsend-ai/fullsend' "$checkout/go.mod" | ||
| test -d "$checkout/cmd/fullsend" | ||
| modfile="${RUNNER_TEMP}/agents.mod" | ||
| cp go.mod "$modfile" | ||
| : > "${RUNNER_TEMP}/agents.sum" | ||
| go mod edit -modfile="$modfile" \ | ||
| -replace="github.com/fullsend-ai/fullsend=${checkout}" | ||
| go mod edit -modfile="$modfile" \ | ||
| -replace="github.com/fullsend-ai/fullsend/internal/mintcore=${checkout}/internal/mintcore" | ||
| go mod tidy -modfile="$modfile" | ||
|
ifireball marked this conversation as resolved.
Resolved
ifireball marked this conversation as resolved.
Resolved
ifireball marked this conversation as resolved.
Resolved
|
||
| { | ||
| echo "FULLSEND_CHECKOUT=$checkout" | ||
| echo "FULLSEND_SHA=$sha" | ||
| echo "BEHAVIOUR_GOFLAGS=-modfile=$modfile" | ||
| } >> "$GITHUB_ENV" | ||
| echo "Using fullsend source ${sha} for ${version}" | ||
|
|
||
| - name: Run behaviour tests | ||
| env: | ||
| E2E_GCP_WIF_PROVIDER: ${{ secrets.AGENTS_BT_E2E_GCP_WIF_PROVIDER }} | ||
| E2E_GCP_SERVICE_ACCOUNT: ${{ secrets.AGENTS_BT_E2E_GCP_SERVICE_ACCOUNT }} | ||
| E2E_GCP_PROJECT_ID: ${{ secrets.AGENTS_BT_E2E_GCP_PROJECT_ID }} | ||
| TEST_FULLSEND_PEM: ${{ secrets.AGENTS_BT_FULLSEND_PEM }} | ||
| TEST_TRIAGE_PEM: ${{ secrets.AGENTS_BT_TRIAGE_PEM }} | ||
| TEST_CODER_PEM: ${{ secrets.AGENTS_BT_CODER_PEM }} | ||
| TEST_REVIEW_PEM: ${{ secrets.AGENTS_BT_REVIEW_PEM }} | ||
| TEST_RETRO_PEM: ${{ secrets.AGENTS_BT_RETRO_PEM }} | ||
| TEST_PRIORITIZE_PEM: ${{ secrets.AGENTS_BT_PRIORITIZE_PEM }} | ||
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.AGENTS_BT_CLOUDFLARE_ACCOUNT_ID }} | ||
| CLOUDFLARE_API_TOKEN: ${{ secrets.AGENTS_BT_CLOUDFLARE_API_TOKEN }} | ||
| TEST_ACTOR_WRITE_PAT: ${{ secrets.AGENTS_BT_ACTOR_WRITE_PAT }} | ||
| TEST_ACTOR_TRIAGE_PAT: ${{ secrets.AGENTS_BT_ACTOR_TRIAGE_PAT }} | ||
| TEST_ACTOR_OUTSIDER_PAT: ${{ secrets.AGENTS_BT_ACTOR_OUTSIDER_PAT }} | ||
| shell: bash | ||
| run: | | ||
| required=( | ||
| E2E_GCP_WIF_PROVIDER E2E_GCP_SERVICE_ACCOUNT E2E_GCP_PROJECT_ID | ||
| TEST_FULLSEND_PEM TEST_TRIAGE_PEM TEST_CODER_PEM TEST_REVIEW_PEM | ||
| TEST_RETRO_PEM TEST_PRIORITIZE_PEM CLOUDFLARE_ACCOUNT_ID | ||
| CLOUDFLARE_API_TOKEN TEST_ACTOR_WRITE_PAT TEST_ACTOR_TRIAGE_PAT | ||
| TEST_ACTOR_OUTSIDER_PAT | ||
|
ifireball marked this conversation as resolved.
Resolved
|
||
| ) | ||
| for name in "${required[@]}"; do | ||
| if [[ -z "${!name:-}" ]]; then | ||
| echo "missing behaviour credential: ${name}" >&2 | ||
| exit 1 | ||
| fi | ||
| done | ||
| make behaviour-test | ||
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| Feature: Agents triage behaviour | ||
|
|
||
| Scenario: Triage applies the ready-to-code label | ||
| Given the enrolled test repository | ||
| And a dummy agent that would: | ||
| | description | op | args | | ||
| | Emit triage JSON | write_fixture | output/agent-result.json, fixtures/triage/sufficient.json | | ||
|
ifireball marked this conversation as resolved.
Resolved
|
||
| And an issue | ||
| When the issue is labeled "ready-for-triage" | ||
| Then the triage workflow completes successfully | ||
| And the run selected the "dummy" runtime | ||
| And the agent will succeed to Emit triage JSON | ||
| And the issue has label "ready-to-code" | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| { | ||
| "action": "sufficient", | ||
| "reasoning": "Issue includes clear reproduction steps and environment details.", | ||
| "clarity_scores": { | ||
| "symptom": 0.9, | ||
| "cause": 0.85, | ||
| "reproduction": 0.9, | ||
| "impact": 0.8, | ||
| "overall": 0.87 | ||
| }, | ||
| "triage_summary": { | ||
| "title": "Login fails", | ||
| "severity": "high", | ||
| "category": "bug", | ||
| "problem": "Application crashes on login", | ||
| "root_cause_hypothesis": "Session handling regression", | ||
| "reproduction_steps": ["Open app", "Enter credentials", "Click login"], | ||
| "environment": "Linux", | ||
| "impact": "Users cannot authenticate", | ||
| "recommended_fix": "Fix session token validation", | ||
| "proposed_test_case": "test_login_success" | ||
| }, | ||
| "comment": "## Triage Summary\n\nThis issue is ready for implementation." | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| //go:build behaviour | ||
|
|
||
| package behaviour_test | ||
|
|
||
| import ( | ||
| "path/filepath" | ||
| "runtime" | ||
| "testing" | ||
|
|
||
| "github.com/fullsend-ai/fullsend/pkg/behaviourtest" | ||
| ) | ||
|
|
||
| func TestBehaviour(t *testing.T) { | ||
| _, sourceFile, _, ok := runtime.Caller(0) | ||
| if !ok { | ||
| t.Fatal("finding behaviour suite source path") | ||
| } | ||
|
|
||
| root := filepath.Dir(sourceFile) | ||
| behaviourtest.RunSuite(t, behaviourtest.SuiteOptions{ | ||
| FeaturePaths: []string{filepath.Join(root, "features")}, | ||
| FixturesRoot: "behaviour", | ||
| }) | ||
| } |
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| # Behaviour testing | ||
|
|
||
| The live behaviour suite runs only against the fullsend `dev` preview driver. | ||
| It never uses the durable stage mint. | ||
|
|
||
| ## Local checks | ||
|
|
||
| The suite is build-tagged and imports fullsend's live runner. The CI workflow | ||
| prepares a temporary module file that points at the exact fullsend checkout | ||
| before running it. With that same setup, a credential-free compile is: | ||
|
|
||
| ```bash | ||
| go test -tags behaviour -run '^$' -exec ./scripts/run-behaviour-test-exec.sh ./behaviour | ||
|
ifireball marked this conversation as resolved.
Resolved
|
||
| ``` | ||
|
|
||
| The live command is: | ||
|
|
||
| ```bash | ||
| make behaviour-test | ||
| ``` | ||
|
|
||
| When `FULLSEND_CHECKOUT` is set, the test process runs from that checked-out | ||
| fullsend source tree. This is required for vendoring when the suite is run from | ||
| the agents module. | ||
|
|
||
| ## CI credentials | ||
|
|
||
| The `dev` GitHub Environment on `fullsend-ai/agents` must contain these | ||
| agent-specific secrets: | ||
|
|
||
| ```text | ||
| AGENTS_BT_E2E_GCP_WIF_PROVIDER | ||
| AGENTS_BT_E2E_GCP_SERVICE_ACCOUNT | ||
| AGENTS_BT_E2E_GCP_PROJECT_ID | ||
| AGENTS_BT_CLOUDFLARE_ACCOUNT_ID | ||
| AGENTS_BT_CLOUDFLARE_API_TOKEN | ||
| AGENTS_BT_FULLSEND_PEM | ||
| AGENTS_BT_TRIAGE_PEM | ||
| AGENTS_BT_CODER_PEM | ||
| AGENTS_BT_REVIEW_PEM | ||
| AGENTS_BT_RETRO_PEM | ||
| AGENTS_BT_PRIORITIZE_PEM | ||
| AGENTS_BT_ACTOR_WRITE_PAT | ||
| AGENTS_BT_ACTOR_TRIAGE_PAT | ||
| AGENTS_BT_ACTOR_OUTSIDER_PAT | ||
| ``` | ||
|
|
||
| These are deliberately separate from existing fullsend `TEST_*`, Cloudflare, | ||
| GCP, PEM, and PAT credentials. Do not rotate or overwrite an existing value. | ||
| Private keys and PATs should be newly created alongside existing credentials; | ||
| the workflow maps the new names to the environment variable names consumed by | ||
| the shared fullsend driver. | ||
|
|
||
| ## Dev pool authorization | ||
|
|
||
| The cross-org e2e role must authorize the agents repository in every dev pool | ||
| organization while retaining the existing fullsend caller: | ||
|
|
||
| ```bash | ||
| # Run from an exact fullsend checkout with the operator credentials required | ||
| # by the fullsend admin command. | ||
| for org in halfsend-{01..12}; do | ||
| go run ./cmd/fullsend admin foreign allow \ | ||
| --org "$org" \ | ||
| --role e2e \ | ||
| --caller fullsend-ai/agents | ||
| done | ||
| ``` | ||
|
|
||
| Verify each organization with: | ||
|
|
||
| ```bash | ||
| go run ./cmd/fullsend admin foreign list --org halfsend-01 | ||
| ``` | ||
|
|
||
| The test Apps and actor permissions must also be present on the dev pool | ||
| organizations. Stage organization permissions are intentionally out of scope. | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.