Skip to content

test: add gated Azure Functions host E2E suite (Storage backend)#303

Merged
YunchuWang merged 5 commits into
mainfrom
yunchuwang-yunchuwang-functions-e2e-tests
Jul 16, 2026
Merged

test: add gated Azure Functions host E2E suite (Storage backend)#303
YunchuWang merged 5 commits into
mainfrom
yunchuwang-yunchuwang-functions-e2e-tests

Conversation

@YunchuWang

@YunchuWang YunchuWang commented Jul 15, 2026

Copy link
Copy Markdown
Member

What

Adds a gated Azure Functions host end-to-end (E2E) test suite plus a dedicated CI workflow. The suite launches a real func start host for a ported sample function app (backed by Azurite / AzureStorage) and drives it over HTTP, exercising the full Node worker ↔ host ↔ Durable-extension path.

Mirrors what durabletask-python #155 added for Python, adapted to TypeScript/jest.

Test app — faithful BasicNode port

test/e2e-functions/test-app/ is a faithful port of the extension repo's own e2e BasicNode app (the 14 Durable function files are body-identical to source, license header aside). It is wired to the published durable-functions ^3.1.0 + @azure/functions ^4.11.2 packages so the suite is genuinely runnable without any in-repo package build. A comment in test-app/package.json documents how to repoint the durable-functions dependency at an in-repo package (e.g. a future packages/azure-functions-durable) later.

Host readiness is detected by polling /admin/host/status for state == "Running", matching the extension's C# FunctionAppProcess fixture.

What it covers

13 area specs, each translated from the corresponding xUnit test class in the extension repo (Tests/Tests/*.cs), using the exact expected strings from NodeTestLanguageLocalizer.cs:

  • hello-cities — activity chaining (Hello Tokyo! / Hello Seattle! / Hello London!)
  • activity-input-type — byte[]/int[]/string/custom-class serialization
  • error-handling — rethrow → Failed; catch → Completed; retry/custom-retry → Success
  • external-event — raise to running / completed / missing
  • suspend-resume — suspend/resume of running, suspended, and terminal instances (Node quirks honored)
  • terminateLongRunningOrchestrator
  • timeoutTask.any activity-vs-timer race
  • rewind — fail N times → rewind → Completed with per-key invocation counts; Node 400s for non-failed instances
  • is-replaying — replay-flag checks
  • large-output
  • orchestration-queryGetAllInstances / GetRunningInstances
  • purge — by instance / by filter
  • class-based-entity — class-based counter state + entity error handling

Node bug annotations from the .cs sources are honored as permanent it.skip / describe.skip with a comment citing the same reason/issue (azure-functions-durable-js #642, #564, #608, #644, #679, plus the dotnet-isolated-only FailureDetails / CustomExceptionProperties cases).

Why it's split out / gated

The suite stays isolated so it never affects main CI or the default test run:

  • Isolated jest config. Specs run only via jest.functions-e2e.config.js + npm run test:e2e:functions:internal. They are not part of npm test / workspaces, test:unit, or test:e2e:internal.
  • Runtime skip guard. A jest globalSetup preflight skips the whole suite cleanly unless func is on PATH, Azurite is reachable on 127.0.0.1:10000, and the test-app is installed + built. Missing any prerequisite → clean no-op (0 failures).
  • CI workflow. .github/workflows/functions-e2e-tests.yaml is currently manual / ad hoc (workflow_dispatch only). When dispatched it installs func + Azurite, builds the test-app, and runs the suite for real. (A scoped pull_request trigger can be re-added later; the header comment documents how.)

Files

  • test/e2e-functions/harness.ts (Node-only FunctionApp lifecycle + HTTP/orchestration/entity helpers mirroring DurableHelpers.cs/HttpHelpers.cs; cross-platform teardown), global-setup.ts / global-teardown.ts (shared-host lifecycle), the 13 *.spec.ts area suites, README.md, and test-app/ (the ported BasicNode app).
  • jest.functions-e2e.config.js — dedicated, not run by default.
  • package.jsontest:e2e:functions:internal (bare jest) + test:e2e:functions (wrapper) scripts, not wired into npm test.
  • scripts/test-e2e-functions.sh — local/manual wrapper (starts Azurite, installs + builds the test-app, runs the :internal jest).
  • .github/workflows/functions-e2e-tests.yaml — ad-hoc CI workflow.
  • .prettierignore, eslint.config.mjs — exclude the vendored test-app so it stays verbatim while root lint/format remain green.

Verification

  • npm ci + npm run build succeed with these changes; tsc type-checks the harness + specs; root npm run lint / prettier clean (test-app excluded as vendored).
  • With func + Azurite present: npm run test:e2e:functions:internal13 suites, 35 passed / 10 skipped, 0 failures.
  • Without prerequisites → suite skips cleanly (0 failures); npm test / test:unit / test:e2e:internal do not pick up the new specs.

Follow-ups (deferred, tracked in the suite README)

Not ported in this PR: DistributedTracing* (needs an OTLP collector), Versioning / EntityVersioning (needs multi-version host config; EntityVersioning is Node-Skip anyway), DedupeStatuses, GetOrchestrationHistory, HTTPFeature, Restart, Scheduled (dotnet-isolated-only scheduled starts).

Related: mirrors durabletask-python#155.

Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com

Add an Azure Functions host end-to-end test suite that launches a real
`func start` host for a sample app (backed by Azurite / AzureStorage) and
drives it over HTTP, mirroring durabletask-python #155. It exercises the
in-repo `durable-functions` package added by #282
(packages/azure-functions-durable), which is not on main yet.

Everything is gated/skipped so existing CI stays green on main until #282
merges:
- The suite runs only via a dedicated jest config
  (jest.functions-e2e.config.js) + `npm run test:e2e:functions:internal`;
  it is not part of `npm test` / workspaces / `test:e2e:internal`.
- Specs skip cleanly unless `func`, Azurite (127.0.0.1:10000) and a
  built/installed test-app are all present (jest globalSetup preflight).
- The new CI workflow gates every job behind the RUN_FUNCTIONS_E2E repo
  variable, so it is a no-op until set to 'true' after #282 lands.

The sample app functions are kept byte-for-byte identical to
azure-functions-durable-js; only the `durable-functions` dependency is
rewired to the workspace package, plus a plain /api/ping readiness function.
The vendored test-app is excluded from root eslint + prettier to preserve it
verbatim while keeping lint/format green.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comment thread .github/workflows/functions-e2e-tests.yaml
Comment thread scripts/test-e2e-functions.sh
Comment thread test/e2e-functions/test-app/src/functions/counter1.ts Outdated
Comment thread test/e2e-functions/test-app/package.json Outdated
Comment thread test/e2e-functions/test-app/src/functions/hello.ts Outdated
Replace the thin 2-function smoke app with a faithful port of the extension
repo's BasicNode app and xUnit E2E suite, wired to the published
durable-functions (^3.1.0) + @azure/functions (^4.11.2) so the suite is
genuinely runnable rather than blocked on an unmerged in-repo package.

App (test-app/): port all orchestrators, activities, class-based entities and
client-operation HTTP triggers verbatim (HelloCities, ActivityInputType,
Suspend/Resume, Terminate, Timeout, Rewind, IsReplaying checks, LargeOutput,
activity/entity error handling, orchestration query, purge), plus Shared/
ExceptionTypes.ts and a generic StartOrchestration starter.

Harness: generalize to mirror DurableHelpers/HttpHelpers (invokeHttpTrigger,
parseInstanceId/parseStatusQueryGetUri, waitForOrchestrationState,
getOrchestrationDetails) and add the Node localizer strings. Keep the shared
single-host lifecycle with func/Azurite preflight + clean skip.

Specs: 13 area specs ported faithfully from the xUnit tests using the Node
localizer strings. Node/Node-DTS skip traits translated (Storage backend keeps
Node-DTS tests); Node bug annotations honored (#642, #564, #608, #645, #679,
#644, and the suspend/resume/terminate-of-terminal swallow quirk).

Infra: remove the unmerged-package dependency and PR#282 gating language from
the workflow, script, eslint/prettier ignores; rewrite the README with the new
scenarios, prerequisites, run instructions and deferred follow-ups.

Verified locally: build + typecheck + eslint + prettier clean; full suite runs
green against a real func host + Azurite (13 suites, 35 passed, 10 skipped).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comment thread test/e2e-functions/test-app/src/functions/ExternalEventOrchestration.ts Dismissed
Comment thread test/e2e-functions/test-app/src/functions/ActivityInputType.ts Dismissed
Comment thread test/e2e-functions/test-app/src/functions/HelloCities.ts Dismissed
Comment thread test/e2e-functions/test-app/src/functions/TimeoutOrchestration.ts Dismissed
Comment thread test/e2e-functions/test-app/src/functions/ping.ts Outdated
YunchuWang and others added 2 commits July 16, 2026 10:29
Drop the automatic pull_request trigger so the gated Functions host E2E
suite runs on demand via workflow_dispatch while it beds in, rather than
gating day-to-day PRs. Document how to re-enable the pull_request trigger
later. Job steps (install func + Azurite, build test-app, run suite) are
unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…robe

The ping.ts HTTP trigger was not part of the extension's BasicNode app; it
only backed the harness readiness probe. Switch the harness to poll the func
host's /admin/host/status endpoint for state == "Running" (matching the
extension's C# FunctionAppProcess fixture) and remove ping.ts so the test-app
is a faithful BasicNode port. Addresses PR review feedback.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e718bd1c-581f-4a9a-995a-62d4a38bbc86
@YunchuWang YunchuWang marked this pull request as ready for review July 16, 2026 17:55
Copilot AI review requested due to automatic review settings July 16, 2026 17:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a gated Azure Functions host E2E test suite that boots a real func start host for a ported sample app (Storage backend via Azurite) and drives Durable behaviors over HTTP. This extends the repo’s testing surface to cover the full Functions host ↔ Node worker ↔ durable-extension path, without impacting default npm test runs.

Changes:

  • Added a new test/e2e-functions/ Jest suite (harness + global setup/teardown + specs) that self-skips unless prerequisites are present.
  • Added a ported Azure Functions “BasicNode”-style test app under test/e2e-functions/test-app used by the suite.
  • Added a dedicated Jest config + npm scripts + convenience runner, plus CI workflow and lint/format exclusions for the vendored app.

Reviewed changes

Copilot reviewed 45 out of 46 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
test/e2e-functions/activity-input-type.spec.ts E2E spec for activity input serialization scenarios.
test/e2e-functions/class-based-entity.spec.ts E2E spec for class-based entity state behavior.
test/e2e-functions/error-handling.spec.ts E2E spec for activity/entity exception and retry behavior.
test/e2e-functions/external-event.spec.ts E2E spec for raising external events over HTTP.
test/e2e-functions/global-setup.ts Jest globalSetup that performs prerequisite checks and starts shared func host.
test/e2e-functions/global-teardown.ts Jest globalTeardown that stops the shared func host and removes preflight file.
test/e2e-functions/harness.ts Core harness: host lifecycle, HTTP helpers, polling, localized strings.
test/e2e-functions/hello-cities.spec.ts E2E spec for HelloCities orchestration over HTTP.
test/e2e-functions/is-replaying.spec.ts E2E spec for isReplaying behavior across orchestrations.
test/e2e-functions/large-output.spec.ts E2E spec for large outputs via status URI + query trigger.
test/e2e-functions/orchestration-query.spec.ts E2E spec for instance query endpoints.
test/e2e-functions/purge.spec.ts E2E spec for purge-by-time and purge-by-entity behaviors.
test/e2e-functions/README.md Suite documentation (gating model, coverage, local run steps).
test/e2e-functions/rewind.spec.ts E2E spec for rewind behavior and invocation counting.
test/e2e-functions/suspend-resume.spec.ts E2E spec for suspend/resume behaviors.
test/e2e-functions/terminate.spec.ts E2E spec for terminate behaviors and Node-localized assertions.
test/e2e-functions/timeout.spec.ts E2E spec for activity-vs-timer timeout race.
test/e2e-functions/test-app/.funcignore Functions packaging ignore rules for the test app.
test/e2e-functions/test-app/.gitignore Test-app-local ignores (dist, node_modules, Azurite artifacts, logs).
test/e2e-functions/test-app/host.json Functions host configuration (extension bundle + durableTask storage provider).
test/e2e-functions/test-app/local.settings.json Local dev settings for Storage + node worker runtime.
test/e2e-functions/test-app/package.json Test-app package definition pinned to published durable-functions and build scripts.
test/e2e-functions/test-app/package-lock.json Locked dependencies for deterministic test-app installs.
test/e2e-functions/test-app/tsconfig.json TypeScript build config for compiling test-app to dist/.
test/e2e-functions/test-app/src/index.ts Functions v4 programming model setup entrypoint.
test/e2e-functions/test-app/src/Shared/ExceptionTypes.ts Ported custom exception types for error-handling tests.
test/e2e-functions/test-app/src/functions/ActivityErrorHandling.ts Ported orchestrations/activities for activity exception/retry coverage.
test/e2e-functions/test-app/src/functions/ActivityInputType.ts Ported orchestrations/activities for input type serialization coverage.
test/e2e-functions/test-app/src/functions/ClassBasedEntities.ts Ported class-based entity test orchestration/entity.
test/e2e-functions/test-app/src/functions/EntityErrorHandling.ts Ported orchestrations/entity for entity exception behavior.
test/e2e-functions/test-app/src/functions/ExternalEventOrchestration.ts Ported external-event orchestration + HTTP event sender.
test/e2e-functions/test-app/src/functions/HelloCities.ts Ported HelloCities orchestration + HTTP starters.
test/e2e-functions/test-app/src/functions/IsReplayingChecks.ts Ported orchestrations/activities for isReplaying scenarios.
test/e2e-functions/test-app/src/functions/LargeOutputOrchestrator.ts Ported orchestration + query trigger for large outputs.
test/e2e-functions/test-app/src/functions/OrchestrationQuery.ts Ported HTTP triggers for instance list queries.
test/e2e-functions/test-app/src/functions/PurgeOrchestrationHistory.ts Ported purge trigger + dummy orchestration/entity used by purge tests.
test/e2e-functions/test-app/src/functions/RewindOrchestration.ts Ported rewind orchestrations/activities/entities + rewind HTTP triggers.
test/e2e-functions/test-app/src/functions/SuspendResumeOrchestration.ts Ported suspend/resume HTTP triggers.
test/e2e-functions/test-app/src/functions/TerminateOrchestration.ts Ported long-running orchestration + terminate HTTP trigger.
test/e2e-functions/test-app/src/functions/TimeoutOrchestration.ts Ported timeout orchestration/activity + HTTP starter.
jest.functions-e2e.config.js Dedicated Jest config to run this suite in isolation.
package.json Adds npm scripts to run the Functions E2E suite (internal + wrapper).
scripts/test-e2e-functions.sh Convenience wrapper that installs/builds test-app and runs the suite.
.github/workflows/functions-e2e-tests.yaml CI workflow to run the suite (currently workflow_dispatch only).
eslint.config.mjs Excludes test-app (and dedicated Jest config) from ESLint.
.prettierignore Excludes ported test-app from Prettier formatting.
Files not reviewed (1)
  • test/e2e-functions/test-app/package-lock.json: Generated file

Comment thread test/e2e-functions/test-app/src/functions/HelloCities.ts
Comment thread test/e2e-functions/test-app/src/functions/HelloCities.ts
Comment thread test/e2e-functions/test-app/src/functions/RewindOrchestration.ts
Comment thread test/e2e-functions/test-app/src/functions/RewindOrchestration.ts
Comment thread .github/workflows/functions-e2e-tests.yaml
Comment thread scripts/test-e2e-functions.sh
kaibocai
kaibocai previously approved these changes Jul 16, 2026
The wrapper used set -uo pipefail (no -e) and did not check the test-app
install/build subshell result, so a failed npm install/build would fall
through to a skipped Jest run and exit 0, masking the failure. Guard the
install/build and propagate a non-zero exit (stopping Azurite first).
Addresses PR review feedback.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e718bd1c-581f-4a9a-995a-62d4a38bbc86
@YunchuWang YunchuWang merged commit f8c460c into main Jul 16, 2026
27 checks passed
@YunchuWang YunchuWang deleted the yunchuwang-yunchuwang-functions-e2e-tests branch July 16, 2026 19:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants