Testing
The extension ships an automated cross-version test matrix. It provisions one throwaway TYPO3 installation per supported major, wires the working tree in as a Composer path repository, and runs the test layers against each one.
Requirements
- DDEV 1.24 or newer, and a running Docker daemon.
- Roughly 6 GB of free disk — each lab is about 600–900 MB of
vendor/plus a database volume. - Git, because three of the labs test a different branch of this repository.
- For the live checks: Node.js 18 or newer (the browser check installs
playwright-intocore Build/on first use).testing/ browser/
Running the matrix
From the extension directory:
make test-matrix
A cold run provisions six labs and takes roughly 30–60 minutes. Warm re-runs reuse the existing labs and take a few minutes per lab.
Useful variants:
make test-matrix # all majors, offline layers only
make test-matrix-live # additionally the live checks, see below
make test-matrix-clean # delete all labs and worktrees
make install-hooks # gate tag pushes on a green matrix
Build/Scripts/runTests.sh --versions=13,14 # a subset
Build/Scripts/runTests.sh --keep-labs # never delete, even when green
Build/Scripts/runTests.sh --layers=unit # one layer only
Build/Scripts/runTests.sh --status # what state is each lab in?
The layer names are resolve, install, canary, unit,
functional, phpstan and cgl.
What is tested where
The matrix is defined in Build/. Three of the six labs check out
a different branch, because the extension is maintained as one release line per
TYPO3 major — see Version Compatibility.
| TYPO3 | Branch tested | PHP | Source wired into the lab |
|---|---|---|---|
| 14.3 | main | 8.2 | the working tree |
| 13.4 | main | 8.2 | the working tree |
| 12.4 | main | 8.1 | the working tree |
| 11.5 | feature- | 8.1 | a git worktree |
| 10.4 | feature- | 7.4 | a git worktree |
| 9.5 | feature- | 7.4 | a git worktree |
Coding standards are checked once per branch, in its newest lab: the older labs
resolve an older typo3/ whose rules contradict the newer ones,
and style is a property of the source, not of the TYPO3 version.
The test layers
Each lab runs these in order — cheapest and most likely to fail first:
| Layer | What it proves |
|---|---|
| Composer resolve | The declared constraints actually resolve against that TYPO3 major. A conflict here is a finding, not an error — it means the advertised compatibility is wrong. |
| TYPO3 install | A real, installed TYPO3 site comes up on that major. |
| Wiring canary | The lab is testing your working tree and not a copy fetched from Packagist. See the warning below. |
| PHPUnit unit | Configuration precedence, the empty-value guard, the
save exemption, credential validation, the sender
resolution order, client/token reuse and error wrapping. On the 1.x
and 2.x lines, the complete send path including retries runs against
a mocked HTTP client. |
| PHPUnit functional | The extension activates, and TYPO3 really selects this transport when
MAIL. is set to the fully-qualified class name. |
| PHPStan | Static analysis at level 8 against that major's core API. |
| Coding standards | TYPO3 CGL via php-cs-fixer. |
| Live checks | Skipped unless explicitly enabled. See below. |
Warning
The wiring canary is not a formality. It writes a temporary file into the
extension source on the host and checks that it appears inside the container. If
the path repository is not wired correctly, Composer silently installs a release
copy from Packagist instead, and every green result in that lab is meaningless.
A failed canary marks the lab wiring and invalidates its results.
Note
TYPO3 11 and 12 are ELTS. Every freely published patch of those majors
carries security advisories, so Composer's audit would refuse to load any of
them and the major could not be tested at all. The harness therefore sets
policy. inside the lab. That is safe for a
throwaway installation, but it means a lab is not a security-current
install — the report's environment header says so on every run.
Known coverage gaps
Important
On the 3.x and 4.x lines, the call into the Microsoft Graph SDK itself is only
executed by the live checks. Everything around it — configuration, sender
resolution, client reuse and the error wrapping — has offline tests through
the
create seam.
The live checks
With -- (make test-), each lab additionally runs four checks
against the real Microsoft Graph API. They use a lab-only fixture from
Build/ that is mounted into the lab and never shipped.
| Check | What it proves |
|---|---|
live- | A real mail is accepted by Graph in CLI context, with the
credentials in TYPO3_. |
browser | A headless Chromium logs into the backend, opens System > Configuration and finds the client secret masked — never in plain text. Screenshots are stored with the report. |
live- | A real mail is accepted by Graph in frontend context, while
TYPO3_ hold no credentials: they come only from
:= get in the root template's setup and constants. This
proves the documented getEnv() configuration
on that TYPO3 version. |
getenv- | A get of a variable that does not exist leaves the send
failing cleanly with missing required field: client. |
Every message has the subject [ex365-, so a run
can be traced in the mailbox. A full run sends two messages per lab.
-
Create the credentials file outside the repository:
mkdir -p ~/.config/ok-ex365 cp Build/testing/.env.test.dist ~/.config/ok-ex365/test.env chmod 600 ~/.config/ok-ex365/test.envCopied!Fill in a test tenant.
EXCHANGE365_defaults to the sender address. SetTEST_ RECIPIENT OK_to use a different file.EX365_ TEST_ ENV -
Run the matrix with the flag:
make test-matrix-liveCopied!
The credentials are passed to the lab's web container through
.ddev/., so they are in the real process environment where
getenv sees them, and that file is deleted again after the checks. No
secret is written into a PHP or TypoScript file.
Note
The live checks surfaced two network failure modes that production servers share: dead IPv6 routes to Microsoft (DNS returns IPv6 addresses the host cannot reach) and connections that stall after connecting. All transports therefore use a 10 s connect and 30 s total timeout — for the Graph call instead of the SDK's 100 s, and for the OAuth token request, which the SDK's OAuth library otherwise sends with no timeout at all (a stalled token request would block a scheduler run forever). A request that never connected is retried once; a request that stalled after connecting is not retried, because Graph may already have accepted the mail and a retry could send it twice.
Release gate
make install-hooks
enables .githooks/ for this clone and all its worktrees. Pushing a
tag then runs the matrix for every TYPO3 major of the branch the tag is on —
including the live checks when the credentials file exists — and refuses the push
unless everything is green. The checkout being tested must be clean and at the
tagged commit, so a green gate always means "this exact release was tested".
Branch pushes are not affected. OK_ bypasses
the gate in an emergency.