-
-
Notifications
You must be signed in to change notification settings - Fork 27
Developer Tests Assets
Part of Developer Tests. Eleven suites covering the asset estate: importing it, extending it with custom fields, what the inventory agent reports, recognising a renamed machine, numbering and labelling it, and the CMDB's typed properties, and the Proxmox and Cloud Director syncs.
All eleven touch the database. The older suites create only rows with their own
prefix and sweep them before and after, including on failure, because the
services they drive open their own transactions. The three added in 3.1.0 can
do better: the reconciliation and tag services join a transaction the caller
already holds, so those suites run inside one that is always rolled back, and
the labels suite only reads. A sweep is only ever by the suite's own prefix β
never a broad pattern like COMP% that could match a real machine.
| Test | Prefix it uses | Needs |
|---|---|---|
asset-import.php |
zzimp |
Database |
asset-custom-fields.php |
zz_af_ |
Database |
asset-physical-disks.php |
ZZPD |
Database + local HTTP |
asset-disk-hiding.php |
ZZDH |
Database + local HTTP |
asset-last-seen-repair.php |
ZZLS |
Database |
cmdb-typed-fields.php |
zz_parity |
Database |
contract-assets.php |
ZZCA |
Database |
asset-reconciliation.php |
ZZREC- |
Database (rolled back) |
asset-tag-numbering.php |
ZZTAG- |
Database (rolled back) |
asset-labels.php |
β | Database (read-only) |
hypervisor-sync.php |
ZZHV |
Database (rolled back) |
Not "does it read a CSV" β it obviously does β but the four things that make an import safe to leave running unattended on a schedule:
- Reconciliation. The same file twice must update, never duplicate, and an ambiguous match must refuse rather than guess.
- Preview. Reports exactly what a live run would do, and writes nothing.
- The holding area. A bad row is kept, with its reason and its source line, so somebody can see what to correct.
- Rows that vanish. Handled by an explicit policy, never a default guess.
Writes a real CSV to the system temp directory and runs the import service
against it, then runs it again to test reconciliation. Sweeps zzimp-prefixed
rows out of asset_import_runs, asset_import_run_entries, assets,
asset_history and the field-set tables before and after.
php tests/asset-import.php
- Reconciliation β a repeat import is now duplicating assets. This is the one that silently doubles a customer's estate overnight, so treat it as blocking.
- Preview wrote something β the preview path has picked up a write. Anyone using preview to check a risky file is now being changed by the check.
- The holding area lost the source line β a rejected row is no longer traceable to the line it came from, which makes a failed import unfixable.
The scenario the feature was built for, end to end, exactly as it was described:
"We buy 10 TVs for our meeting rooms and on day 1 my manager says record make, model and size. Then 6 months later he wants to pilot making SOME of them smart β can I add IP address, MAC address and Netflix enabled to SOME of the TVs and leave the others?"
So the test buys ten televisions, records three fields on all of them, then six months later adds three more fields to three of them, and asserts the other seven are untouched β no rows, no fields, nothing to fill in.
Drives AssetFieldsService directly. errOf() captures ServiceError messages
so refusals can be asserted by their text. It never edits an existing field set,
only zz_af_-prefixed ones.
php tests/asset-custom-fields.php
The assertion about the other seven is the important one. If adding fields to a subset touches assets outside that subset, the feature has become "add these fields to everything", which is the opposite of what it is for.
Physical disks reported by the inventory agent. The agent has sent
disks.physical β model, serial, size, media type, interface β since its
first version, and the ingest endpoint read only disks.logical and dropped
the rest on the floor.
π It drives the real ingest endpoint over HTTP, with a real agent-shaped payload, auth header and JSON body β because that is how the agent reaches it. A direct include would skip half of what can go wrong. It then reads the data back the way the asset screen does, not the way the writer wrote it.
Creates an asset and an API key, both prefixed ZZPD, and removes them plus
whatever rows the endpoint wrote.
php tests/asset-physical-disks.php
Needs the app reachable at http://localhost/freeitsm-app, or set
FREEITSM_BASE_URL.
If every assertion fails at once, check the base URL first β the test is probably not reaching the app at all. A single field missing means the ingest endpoint has stopped reading that part of the agent's payload.
Hiding a physical drive you do not care about.
π΄ The test that matters is "a hidden drive stays hidden after the agent reports
again". asset_physical_disks is cleared and rewritten on every run and the
row ids are reissued, so any implementation that remembers a disk by id, or by
a column on its row, passes every other test here and then fails silently, hours
later, on a customer's scheduled task.
By genuinely re-posting to the real ingest endpoint, not by simulating one. That is the only way to exercise the clear-and-rewrite the agent actually causes.
php tests/asset-disk-hiding.php
Needs the app reachable over HTTP, as above.
If only the "stays hidden after the agent reports again" assertion is red, something is identifying a disk by a value that does not survive a re-report. A hidden disk must be remembered by something stable β its serial or model β not by its row.
The repair that db_verify.php runs on upgrade. Creating an asset by hand used
to stamp last_seen as well as first_seen, as though something had reported
it. Once last_seen went on screen, every television and SIM card in every
install started reading "21 days ago" in amber β and had been inflating the
Watchtower "not seen" count all along.
It pins down five things:
- the repair fires on a hand-added asset that never reported
- π΄ it never touches an asset an agent has reported
- the date is not destroyed, only de-duplicated β
first_seenkeeps it - it is idempotent, so it needs no run-once flag
- the preview counts the same rows the repair updates β the two are maintained by hand in different files and will drift the first time somebody edits one of them
php tests/asset-last-seen-repair.php
- "does not touch a reported asset" β the repair has become too broad and is rewriting real agent data. Blocking.
- Preview/repair disagree β exactly the drift the last assertion exists to catch. Whoever edited one file must edit the other; the preview is what an admin reads before agreeing to the change.
A parity test for the CMDB's property write path after the typed-field engine was extracted. Three things must be identical to before the extraction:
- every type stores in the right column
- every validation still fires
- every error message is byte-identical β they are the REST API's published error bodies, so a reworded message is a breaking API change
Creates only zz_parity-prefixed classes and objects, and sweeps before and
after.
php tests/cmdb-typed-fields.php
A wrong column means data is being written where nothing will read it. A changed error message means an API consumer that matched on the old text has broken β change it back, or treat it as a deliberate API change and document it.
Assets covered by a contract β every query in includes/contract_assets.php
against the real database.
π It checks the guards from the attacker's side as well as the happy path.
A scoped list is not a gate: a list that only shows you your own records says
nothing about what happens when someone asks directly for a record that is not
theirs. So the gate is tested separately, with a refuses() helper that asserts
a call throws.
php tests/contract-assets.php
A refuses() assertion going red means a guard has stopped refusing β someone
can attach or read an asset across a boundary. A happy-path failure with the
guards still green usually means a query changed shape; read the label for which.
3.1.0, from Sandy's PR #164. How an incoming device is matched to an existing asset β serial before hostname β and the Intune link rules. 24 checks inside one transaction that is always rolled back: possible here because createDiscoveredAsset() and the tag service join a transaction the caller already holds. Covers placeholder serials, a renamed machine found by serial, the ambiguity guard, the laptop refresh (a new serial under an old name is a new asset), rename collisions, company isolation, and Intune (a moved asset keeps its link; an unlinked device links by serial with no stub). See Asset reconciliation β Developer Guide.
php tests/asset-reconciliation.php
3.1.0. AssetTagsService: formats, the counter, the one lock, forward-only, scope, and a failed create giving its number back. 29 checks. Settings come from AssetTagsService::withSettings(), never the live rows; the writes are in one rolled-back transaction. See Asset tag numbering β Developer Guide.
php tests/asset-tag-numbering.php
3.1.0. Label fields and their translated names, the asset tag always printed first, QR error correction for a logo, the sheet sizes, and the label URL built on publicBaseUrl(). 20 checks, read-only: it changes no setting, because a test that rewrote the install's public address would leave every emailed link broken if it crashed half-way.
php tests/asset-labels.php
3.1.0, from Andrew's PR #167. The Proxmox VE and VMware Cloud Director syncs, run for real against a stand-in of each API answering through HypervisorHttp::$testTransport, so no server is needed. 48 checks in one transaction that is always rolled back, most of them about what gets deleted: a Proxmox node whose qemu list failed keeps its VMs, an offline node keeps its VMs, the safety guard, Director read past its own page cap, a failed page removes nothing, edge gateways only after every page. Also https only, the secret stored encrypted and never returned, both Proxmox logins, IPs only from the VM's own NICs, Director's logout, and an unreachable server named as such. With the PR's original deletion rules put back, 7 checks fail. See Proxmox and Cloud Director β Developer Guide.
php tests/hypervisor-sync.php
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