test: add gated Azure Functions host E2E suite (Storage backend)#303
Merged
Conversation
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>
YunchuWang
commented
Jul 15, 2026
YunchuWang
commented
Jul 15, 2026
YunchuWang
commented
Jul 15, 2026
YunchuWang
commented
Jul 15, 2026
YunchuWang
commented
Jul 15, 2026
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>
YunchuWang
commented
Jul 16, 2026
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
Contributor
There was a problem hiding this comment.
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-appused 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
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
kaibocai
approved these changes
Jul 16, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
What
Adds a gated Azure Functions host end-to-end (E2E) test suite plus a dedicated CI workflow. The suite launches a real
func starthost 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
BasicNodeporttest/e2e-functions/test-app/is a faithful port of the extension repo's own e2eBasicNodeapp (the 14 Durable function files are body-identical to source, license header aside). It is wired to the publisheddurable-functions ^3.1.0+@azure/functions ^4.11.2packages so the suite is genuinely runnable without any in-repo package build. A comment intest-app/package.jsondocuments how to repoint thedurable-functionsdependency at an in-repo package (e.g. a futurepackages/azure-functions-durable) later.Host readiness is detected by polling
/admin/host/statusforstate == "Running", matching the extension's C#FunctionAppProcessfixture.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 fromNodeTestLanguageLocalizer.cs:hello-cities— activity chaining (Hello Tokyo!/Hello Seattle!/Hello London!)activity-input-type— byte[]/int[]/string/custom-class serializationerror-handling— rethrow → Failed; catch → Completed; retry/custom-retry →Successexternal-event— raise to running / completed / missingsuspend-resume— suspend/resume of running, suspended, and terminal instances (Node quirks honored)terminate—LongRunningOrchestratortimeout—Task.anyactivity-vs-timer racerewind— fail N times → rewind → Completed with per-key invocation counts; Node 400s for non-failed instancesis-replaying— replay-flag checkslarge-outputorchestration-query—GetAllInstances/GetRunningInstancespurge— by instance / by filterclass-based-entity— class-based counter state + entity error handlingNode bug annotations from the
.cssources are honored as permanentit.skip/describe.skipwith a comment citing the same reason/issue (azure-functions-durable-js #642, #564, #608, #644, #679, plus the dotnet-isolated-onlyFailureDetails/CustomExceptionPropertiescases).Why it's split out / gated
The suite stays isolated so it never affects
mainCI or the default test run:jest.functions-e2e.config.js+npm run test:e2e:functions:internal. They are not part ofnpm test/ workspaces,test:unit, ortest:e2e:internal.globalSetuppreflight skips the whole suite cleanly unlessfuncis onPATH, Azurite is reachable on127.0.0.1:10000, and the test-app is installed + built. Missing any prerequisite → clean no-op (0 failures)..github/workflows/functions-e2e-tests.yamlis currently manual / ad hoc (workflow_dispatchonly). When dispatched it installsfunc+ Azurite, builds the test-app, and runs the suite for real. (A scopedpull_requesttrigger can be re-added later; the header comment documents how.)Files
test/e2e-functions/—harness.ts(Node-onlyFunctionApplifecycle + HTTP/orchestration/entity helpers mirroringDurableHelpers.cs/HttpHelpers.cs; cross-platform teardown),global-setup.ts/global-teardown.ts(shared-host lifecycle), the 13*.spec.tsarea suites,README.md, andtest-app/(the portedBasicNodeapp).jest.functions-e2e.config.js— dedicated, not run by default.package.json—test:e2e:functions:internal(bare jest) +test:e2e:functions(wrapper) scripts, not wired intonpm test.scripts/test-e2e-functions.sh— local/manual wrapper (starts Azurite, installs + builds the test-app, runs the:internaljest)..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 buildsucceed with these changes;tsctype-checks the harness + specs; rootnpm run lint/ prettier clean (test-app excluded as vendored).func+ Azurite present:npm run test:e2e:functions:internal→ 13 suites, 35 passed / 10 skipped, 0 failures.npm test/test:unit/test:e2e:internaldo 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;EntityVersioningis 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