-
-
Notifications
You must be signed in to change notification settings - Fork 27
Developer Tests Integrations
Part of Developer Tests. Issue trackers, AI providers, CalDAV, Microsoft Graph mail folders, and the inventory agent's ingest endpoints.
π The recurring idea on this page: test the decision, not the network. Most
of these suites deliberately stop at the boundary β they prove what FreeITSM
sends and decides, using a fixture or a local stand-in, and say plainly what
they cannot prove. A green run here is not a working integration; it is a correct
request. The exception is caldav-provider.php, which really does drive a server.
| Test | Needs |
|---|---|
integrations/run.php |
Nothing |
integrations/templates_check.php |
Database |
azure-openai/run.php |
Nothing (starts its own server) |
caldav-provider.php |
A running Baikal fixture |
mailbox-folder-resolve.php |
Nothing |
agent-empty-report-guard.php |
Database |
IssueDoc and the provider contract for external issue trackers.
IssueDoc is the piece every later phase writes through β descriptions first,
comments next, everything after. If it is wrong, the next phase is where you find
out and where you would have to rip it up. So it gets a real suite now rather
than an eyeball.
Four renderers produce four genuinely different syntaxes from one document, and the risk is that they drift β one quietly stops escaping, or handles an empty paragraph differently. So every case runs through all four and each is asserted separately.
php tests/integrations/run.php
353 assertions. No database, no network, pure functions β fast and safe to run anywhere. This is the best test to run first on a machine you have just cloned to.
Ends with a passed: / failed: block rather than the one-line summary most
other tests use.
The label names the renderer and the case. Fix the one renderer β do not "normalise" all four to agree, because they are supposed to differ; they target different syntaxes.
That every workflow starter recipe can actually run.
workflow/includes/templates.php promises in its own header that "every
trigger_event here is a real, wired trigger β¦ and every action type is a real
handler β a recipe that can't actually run would be worse than no recipe at
all". Nothing enforced that, and the first tracker recipe shipped with an
add_note action that does not exist (it is add_ticket_note).
For every template, not just the tracker ones:
- the trigger exists
- every action type exists
- every arg name exists on that action
- every required arg is supplied, or is a
$configuremarker the user fills in - it resolves against this install without throwing
php tests/integrations/templates_check.php
run.php on purpose. WorkflowEngine::availableActions()
reads webhook formats from the database, and run.php's whole value is that it
needs no database and no network. Do not merge this back into it.
A recipe offers an action or an argument that does not exist. Someone renamed a handler without updating the templates β fix the template, since the handler name is the real one.
Azure OpenAI's deployment-based endpoints. Exactly three things are new compared
with the OpenAI path this codebase has shipped for months: the URL shape, the
api-key header, and the absence of model β and all three are things we
emit, so all three are assertable.
It also checks the three ways an administrator might paste an endpoint (with or
without a trailing slash, with or without /openai) all normalise to the same
URL, and that a deployment name containing a space is escaped β an unencoded
space breaks the request line.
It starts its own php -S on a free port, serving only the
tests/azure-openai/ directory, and stops it again at the end. mock.php is a
stand-in Azure endpoint that records the request it received; the test then reads
that back and asserts on the bytes we sent. The real cURL path is exercised
rather than stubbed.
The mock is the one file in tests/ allowed to answer HTTP, and it accepts
only the built-in server β it refuses under Apache, nginx and php-fpm.
β οΈ On Windowsproc_open()must be givenbypass_shell, or the server is a grandchild ofcmd.exeandproc_terminate()leaves it running.
php tests/azure-openai/run.php
31 assertions. Needs nothing else running.
π΄ It ends with a warning, and the warning is the point:
Green here means WE SEND THE RIGHT REQUEST. It does not mean Azure accepts it β that needs a real tenant.
Nobody on the project has an Azure subscription to point it at. Content filtering, api-version drift, quota and regional behaviour are all outside what this can prove. Debug tool D014 exists for that gap: it makes one live call and reads back Azure's answer.
"the mock server never came up" is an environment problem, not a code one β check nothing is blocking loopback sockets. Anything else means the request we build has changed shape.
The CalDAV provider against a real server. The cases that matter:
- an edit keeps what the analyst added (a reminder) β the rule this provider exists to keep
- a stale change token gives a baseline, never "everything was deleted"
- a calendar address on another host is refused before any request β the per-analyst address must not become a way to reach anywhere
- the analyst's own appointments are never reported, only ours
It needs the Baikal fixture, seeded:
docker compose -f docker/carddav-test/docker-compose.yml up -d
bash docker/carddav-test/seed.sh
php tests/caldav-provider.php
Touches no database. Everything it writes is a FreeITSM-named event in the
throwaway itsm and tech2 calendars, and it deletes what it made.
If you see:
FAIL the address is a calendar server <- Failed to connect to localhost port 8092
Is the Baikal fixture running and seeded?
β¦the fixture is not up. That is an environment failure, not a code failure β start the fixture and run it again.
The "stale token gives a baseline" case is the one with teeth: getting it wrong means a sync deletes a calendar's contents rather than resynchronising it.
mailboxResolveFolderId() β reading mail from a folder that is not the Inbox.
/mailFolders/<x>/messages does not take folder names. Graph accepts a short
list of well-known aliases there and treats everything else as an opaque folder
id, so a mailbox told to read freeitsm got:
400 ErrorInvalidIdMalformed β "Id is malformed."
INBOX had always worked purely because it is on the alias list. Reading a
folder by name had never worked for any name off it.
The resolver takes its HTTP fetcher as an argument, so the whole thing runs
against a fixture with no network and no mailbox. The fixture deliberately
contains a custom top-level folder and a same-named one inside the Inbox, so
freeitsm and Inbox/freeitsm must not resolve to the same place.
What it cannot prove is what Graph really returns.
php tests/mailbox-folder-resolve.php
15 assertions.
A path resolving to the wrong folder means a mailbox silently collects from somewhere else β which looks to the operator like mail going missing.
That an empty agent report does not empty the machine's inventory.
π΄ Both ingest endpoints wipe an asset's hardware tables and reinsert on every
report. The DELETE ran unconditionally while the INSERT was guarded by
!empty(...) β so a report that collected nothing for a category emptied that
category and left it empty. A WMI blip took 226 Device Manager rows with it,
with no error anywhere, until the next good run. Any client holding an API key
could do it deliberately with {"disks":{}}.
An empty list now means "I did not find out", not "there is nothing there": the wipe is skipped and the section is named in the response, because looking like a successful sync of nothing is the other half of the bug.
php tests/agent-empty-report-guard.php
ZZEG-prefixed, cleaned up including on failure.
This one destroys customer data silently and is only noticed later. Treat any red here as blocking, and check both ingest endpoints β the bug was in both.
FreeITSM β an open-source IT Service Management platform Β· github.com/edmozley/freeitsm Β· MIT licence
- Installation
- β° Scheduled tasks (cron jobs)
- Architecture
- π§ͺ Developer tests
- AI Providers
- Internationalisation (i18n)
- Timezones & Time Handling
- π Date & Time Formats
- Theming & Dark Mode
- ποΈ Recent β getting back to what you were doing
- β¨οΈ Command palette (βK)
- π Searching inside tickets
- π Attached documents
-
MobileβFriendly
- β³ π« Mobile: Tickets
- β³ π» Mobile: Assets
- β³ π Mobile: Calendar
- β³ π Mobile: Knowledge
- β³ π¦ Mobile: Service Status
- β³ πΌ Mobile: Watchtower
- β³ π§© Mobile: Problem Management
- β³ π Mobile: Change Management
- β³ πΏ Mobile: Software
- β³ β Mobile: Tasks
- β³ π Mobile: Forms
- β³ π Mobile: Contracts
- β³ π Mobile: Domains
- β³ π Mobile: People
- β³ π Mobile: LMS
- β³ πΊοΈ Mobile: CMDB
- β³ πΊοΈ Mobile: Network Mapper
- β³ π§ Mobile: Process Mapper
- β³ βοΈ Mobile: Workflow
- β³ π₯οΈ Mobile: System
- β³ π Mobile: Reporting
- β³ π Mobile: System Wiki
- β³ π Mobile: Self-Service Portal
- β³ π§° Mobile: Techniques & Tricks
-
Security
- Layer 1 β which modules you can enter
- β³ π§© Module Access Control
- β³ π οΈ Module Access β Developer Guide
- Layer 2 β what you can administer
- β³ π Roles & Permissions
- β³ π οΈ Roles β Developer Guide
- β³ π€ Why capabilities are constants
- Layer 3 β the System module
- β³ π Admin Access Control
- Hardening
- β³ π Security review response 2026-08
- β³ π‘οΈ Security hardening 2026-08
- β³ π οΈ Security hardening 2026-08 β Developer Guide
- β³ π‘οΈ Round three β plain English
- β³ π οΈ Round three β Developer Guide
- β³ π‘οΈ CSRF protection (S4) β Developer Guide
- Single Sign-On (SSO)
- ποΈ LDAP & Active Directory
- π CardDAV contact sync
- Browser Extension
- API Reference
-
π REST API β how it works
- β³ π« REST API: Tickets
- β³ π» REST API: Assets
- β³ π΄ REST API: Problems
- β³ π REST API: Changes
- β³ π REST API: Knowledge
- β³ β REST API: Tasks
- β³ ποΈ REST API: CMDB
- β³ π REST API: Contracts
- β³ ποΈ REST API: Calendar
- β³ πΏ REST API: Software
- β³ π REST API: Domains
- β³ π¦ REST API: Service Status
- β³ βοΈ REST API: Morning Checks
- β³ π REST API: Forms
- β³ βοΈ REST API: Workflow
- β³ π·οΈ REST API: Cost centres
- β³ πΊοΈ REST API: Network Mapper
- β³ π§ Using the API docs page
- β³ π OpenAPI specification
- β³ β OpenAPI: kept correct
- β³ π οΈ Maintaining the catalogue
- Watchtower
-
Tickets
- β³ π Rota copy and paste β Developer Deep Dive
- β³ β Checklists & SOPs
- β³ βοΈ Mandatory fields
- β³ π·οΈ Ticket categories
- β³ π₯ Assigning tickets to a team, and escalation
- β³ π’ One board across every company
- β³ Mailbox Authentication
- β³ π€ Email send log
- β³ Basic IMAP mailboxes
- β³ Email rendering & images
- β³ SLA Management
- β³ WhatsApp channel
-
β³
βοΈ Telegram channel - β³ β CSAT company scope and filters β Developer Guide
- β³ π₯ Microsoft Teams channel
- β³ π¨οΈ Mattermost channel
- β³ π¬ Web chat channel
- β³ π£ Slack channel
- β³ π Linking tickets
- β³ β Record previews
- β³ π Ticket notes: internal or shared
- β³ ποΈ Canned responses
- β³ βοΈ Limiting replies to particular senders
- β³ π¨ Telling the analyst a ticket is theirs
- β³ βοΈ Email signatures
- β³ π The public web address
- β³ π’ Ticket numbering
- β³ π Raising a ticket for someone else
- β³ π Merging tickets
- β³ π Confidential tickets
- β³ π₯ Portal managers
- β³ π Who has seen a ticket
- β³ π Reading long tickets
- β³ β Splitting tickets
- β³ β Selecting several tickets
- β³ ποΈ The folder pane
- β³ π½ Just my tickets, or no closed ones
- β³ π οΈ Snoozing tickets β Developer Guide
- β³ π₯ Collision detection
- β³ β±οΈ Time tracking
- β³ π Scheduled work in your own calendar
- Problem Management
- Tasks
-
Assets
- β³ π’ Moving an asset between companies
- β³ π Shared asset locations
- β³ π§βπΌ Assigning assets to analysts
- β³ π Warranty and lease alerts
- β³ π Saved table views
- β³ π¨οΈ Recording anything, and importing it
- β³ π·οΈ QR asset labels
- β³ π Who holds what, and handover documents
- β³ π₯οΈ The inventory agent (PowerShell)
- β³ ποΈ Proxmox VE servers
- β³ βοΈ VMware Cloud Director servers
- β³ π Linking equipment to tickets
- β³ βοΈ Follow-up tasks on a ticket
- Knowledge
- Change Management
- Calendar
- Morning Checks
- Reporting
- Software
-
Forms
- β³ π¨ The form designer β Developer Guide
- β³ π Layout & the grid β Developer Guide
- β³ ποΈ Collections β grouping submissions
- β³ π Submissions as PDFs
- β³ β‘ What happens next β a form's own actions
- β³ π οΈ Sections & conditional logic β Developer Guide
- β³ π οΈ Lookup fields β Developer Guide
- β³ π‘οΈ Catalogue request approvals
- People
- Domains
- Contracts
- Service Status
- π Notifications
- π¨ War Room
- Self-Service Portal
- LMS
- Process Mapper
- CMDB
- Network Mapper
- Workflows
- Issue trackers (Jira, Azure DevOps)
- System
-
Overview
- β³ π Progress tracker
- β³ Concepts & vocabulary
- β³ Email routing & mailboxes
- β³ Settings: global vs per-company
- β³ Users & self-service
- β³ Staff cross-company access
- β³ π’ One board across every company
- β³ Worked examples
- β³ Pitfalls & gotchas
- β³ Scope: what it's for
- β³ π οΈ Developer Guide (make a module multi-company)
- β³ ποΈ Case study: CMDB (a linked graph)
- β³ π§ͺ Test harness (prove it's isolated)
- What this is
-
π Bugs resolved
- β³ π’ Chat tickets ignored your ticket numbering
- β³ π Dates shown as a dash, or in server time
- β³ π Assets β Users showed people from other companies
- β³ π Restricted analysts could read other modules' data
- β³ πΌοΈ Replies with a picture in the thread failed to send
- β³ π Reply attachments never reached the customer
- β³ π οΈ Outbound email attachments β Developer Guide
- β³ π A global SSO provider was missing from the portal
- β³ π Behind a proxy, the SSO redirect said http
- β³ βοΈ The portal tagline moved when you saved it
- β³ π¨ The portal settings screen forgot what you saved
- β³ π‘οΈ The approvals inbox said "Error" and nothing else
- β³ π A table's answers were missing from the PDF
- β³ β A single-select column let you tick every option
- β³ π The portal ignored a form's field widths
- β³ π The tasks board stopped taking clicks
- β³ ποΈ #121 The index list is out of date after upgrading
- β³ π #133 The calendar subscription was empty
- β³ π #131 Tasks always reopened on the board
- β³ π₯ #129 Every page returned HTTP 500 after upgrading
- β³ π³ #127 A PHP warning above the System page
- β³ π #126 Notes stamped with the server's clock
- β³ π Storing every date in UTC
- β³ πͺ The portal was down for everyone signed in
- β³ βοΈ #120 Workflow notes could never be written
- β³ βοΈ #123 Three errors when running Database Verification
- β³ π #122 The description box was a stub in the corner
- β³ π£ Demo data deleted real accounts
- β³ π #117 Sign-in redirected to the wrong address
- β³ π¨ #108 The priority dot was invisible
- β³ β±οΈ #116 Time logged from the right-click menu
- β³ π #114 API keys refused by our own guard
- β³ ποΈ #110 Assigning a task told nobody
- β³ πͺ #107 Signed out while still working
- β³ π #103 "Share with Requester" reached nobody
- β³ π #102 Search found nothing for hyphens
- β³ πͺ #101 Source code editor opened behind
- β³ βοΈ #88 Subtasks could not be ticked off
- β³ π» #84 Asset deep link selected nothing
- β³ π« #79 A new ticket arrived with no status
- β³ π§ #79 A ticket from email did not say so
- β³ π #78 Bell opened to nothing
- β³ π¬ #77 Mail only collected from Inbox
- β³ π #74 The default password could not be changed
- β³ π¦ #70 Renaming an impact level
- β³ π€ #67 App-only mailboxes could not send
- β³ π #45 Verify only ever worked for Microsoft
- β³ π #45 IMAP reported as not authenticated
- β³ βοΈ An email template stopped escaping itself
- β³ π The portal dashboard showed the wrong time
- β³ π’ The folder said 99 and the list showed 96