Skip to content

Test catalogue

This reference lists automated suites, cases, and CI coverage. See Testing for execution and limitations and Testbed for fixture setup. Every fixture is synthetic; no suite connects to a real vCenter.

At a glance

Tier Command CI job Runs Simulates
Unit and package go test -race ./... build (Linux, macOS, Windows) every push and PR pure logic, persistence, CLI wiring, one in-process simulator per test
In-process simulator same command, tests/ package build every push and PR the real CLI against govmomi/simulator, direct and through SOCKS5/HTTP proxies
Multi-vCenter integration go test -tags integration -run '^TestVCSIM' ./tests/... integration-vcsim every push and PR several independent vCenter processes, one of which can be killed
TUI scenarios scripts/testbed test tui-scenarios (Linux, macOS, Windows) every push and PR the TUI model driven key by key, with render goldens
PTY journeys scripts/testbed pty tui-pty (Linux) every push and PR the real testbed binary in a real terminal
Fuzzing seeds: go test ./...; campaigns: go test -fuzz seeds in build; campaigns in fuzz.yml seeds always; campaigns nightly and on PRs that touch fuzzing arbitrary keys, messages, files, and expressions
Kubernetes end-to-end scripts/test-kubernetes.sh kubernetes-e2e weekly; PRs that change backend or deploy files the shipped CronJob in a kind cluster against two vcsim Services
Release snapshot goreleaser release --snapshot release-snapshot every push and PR the full release build without publishing
Vulnerability scans scripts/vulnscan Go vulnerabilities; scans in release-snapshot every PR; daily rescan reachable Go vulnerabilities, fixable High/Critical image findings, stale databases, expired exceptions
Action pins scripts/check-action-pins.sh Pinned actions every PR an action or reusable workflow referenced by tag or branch, or without a version comment
Release pins scripts/check-release-pins.sh build (Linux) every push and PR drift between Dockerfile, goreleaser, docs, and the CronJob

The build job also runs gofmt, go mod tidy, go vet, the labeler workflow tests (node --test scripts/test-labeler.cjs) and a coverage report. staticcheck runs as its own job, and docs.yml builds this site with mkdocs build --strict.

Unit and package tests

Untagged _test.go files run on Linux, macOS, and Windows with -race. Packages needing a vCenter use an in-process simulator.

Package What it covers Situations simulated
internal/assessment History, capture selectors (ID, label, date), diffs, trends, capacity, timelines, policy, perf, migrations interruption, writer leases, renamed/moved VMs, missing contexts, failed collections, old schemas
internal/cli Commands, flags, exits, JSON, examples invalid inputs, offline import/export, no clobbering, add-context credential choices without a keyring
internal/config Load/save validation, thumbprints future versions, duplicate names, dangling via, a leftover [ssh] table kept and noticed
internal/contextops CLI/TUI context saving keyring cleanup only after save and when requested; shared keys preserved; cleanup failure cannot undo edit
internal/credentials Keyring, prompt, env, file, exec sources lookup-only probing, isolated resolvers, unset/empty variables, missing/failing/silent/slow helpers, no unattended prompts, concise failure reasons
internal/decommission The VM decommission check strict blockers, unknown coverage, ambiguous identity, unresolved dependencies
internal/demo Offline demo estate referential integrity, event log agrees with stored history, duplicate names, five seeded runs with health findings, welcome opt-out
internal/health Health, readiness, orphan/zombie disks thresholds, empty/partial/truncated evidence, duplicate datastore names, schema gates, passthrough
internal/humanize, internal/limiter, internal/uistate Formatting, bounded concurrency, persisted TUI state cancellation while waiting for a slot, corrupt or missing state files, state from older releases
internal/metareport Saved metadata reports unreadable sources produce undetermined membership; failed collections cannot imply removal
internal/network Cross-vCenter network comparison VLAN gaps, MTU mismatches, a blind distributed switch, ambiguous cluster names
internal/perf Performance summaries and sizing classification missing samples that are not zero, too few samples, periodic peaks vs sustained low use, contention
internal/jsonpath -o jsonpath, custom-columns and --sort-by templates ranges, filters, slices, missing fields printing nothing, unsupported syntax refused
internal/query --where predicates, -l and --field-selector numeric comparisons, type errors, named tag/custom fields, set and existence selectors, unread tags matching nothing
internal/report XLSX/CSV, sharing, licenses, vFileInfo, vSource deterministic export, fail-closed pseudonymization, key redaction, old evidence, row limits
internal/rvimport RVTools import missing/extra fields, duplicate identities, size limits, malformed/repeated imports, no mutation client
internal/session Operation deadlines and streaming idle timeouts long work that keeps streaming, silent streams, timeout errors naming their stage
internal/sizing Destination sizing (N-1, growth, storage) partial coverage that is never fit, shared RDMs counted once, missing assumptions reported as unknown
internal/testbed The loopback testbed lab itself authenticated routes start, and wrong fixture credentials are rejected
internal/topology Topology and blast-radius queries shared datastores across contexts, same-named objects that must stay distinct, blind contexts that downgrade results
internal/transport Direct, SOCKS5, and HTTP(S) proxy dialers offline proxies, ambient proxy variables that must be ignored
internal/tui Browse/detail, dashboards, history, datastore browser, prompts, context forms stale replies, narrow renders, failed reloads, refresh tiers, demo restrictions, SSH destinations and argument safety, unavailable keyring, missing credential diagnosis/edit
internal/vsphere Read-only simulator inventory paging, failed/denied collections, provenance, perf gaps, key-free license metadata
cmd/vsfleet-demo The demo binary seeded assessment history is wired in
cmd/vsfleet-vulngate, internal/vulngate Vulnerability gate verdicts and exit codes synthetic govulncheck and grype reports, reachability levels, exceptions by alias, expiry, stale or missing databases

In-process simulator tests

The untagged tests/ suite drives the CLI against an in-process govmomi/simulator in the build job.

File Situation Asserts
cli_test.go context lifecycle, get all across vCenters, one vCenter failing inventory still lists, failures are isolated, vsfleet with no subcommand opens the UI
cli_credentials_test.go, cli_keyring_test.go, contextops_test.go env:, file:, exec: credentials, keyring storage, config set-context with --password-stdin non-interactive misses never read stdin, read-only schemes reject --password-stdin, a failed connection test blocks saving unless overridden
socks5_test.go, httpproxy_test.go SOCKS5 with and without remote DNS, HTTP/HTTPS proxies, basic auth wrong proxy credentials fail, offline proxies fail clearly, untrusted HTTPS proxy certificates are rejected, TLS thumbprint mismatch and discovery
timeout_test.go a vCenter that stops answering mid-enumeration --timeout bounds the whole listing
session_invalidation_test.go a context edited or removed while a session is open the edited context talks to the new vCenter; removal closes the session
readonly_test.go every command run against a SOAP-auditing simulator no command issues a mutating call, and no mutation-capable govmomi package is imported
cli_exitcode_test.go complete and partial captures partial exits 0 by default and 3 with --fail-on-partial
cli_dangling_network_test.go a VM NIC still backed by a deleted port group the capture stays complete and the stale backing is an informational finding, not permission-hidden inventory
cli_hidden_inventory_test.go live get of networks, switches, port groups, hosts and host subresources when VMs or a cluster's host count contradict the listing the gap is reported instead of No resources found., the exit stays 0, and a deleted port group is not a gap
cli_get_test.go, cli_get_output_test.go, cli_get_run_test.go, cli_describe_test.go, cli_snapshot_test.go, cli_network_test.go, cli_topology_test.go, cli_events_test.go, cli_dvswitch_resourcepool_test.go, cli_host_inventory_test.go, cli_host_subresource_test.go get argument forms and globs, get output formats, tag and field selectors, sorting and watch, get and describe from a capture with --run while vCenter is down, describe, snapshots, networks, topology and its table layout, events when every event is routine, distributed switches, resource pools, host storage and network sheets stable identities in JSON, ambiguity across contexts until narrowed, host-scoped device evidence that never leaks across hosts
cli_datastore_files_test.go, cli_file_inventory_test.go datastore browsing and the opt-in file inventory a default capture never browses, limit flags do not enable browsing, denial and truncation are visible in human and JSON output, export is byte-identical
cli_license_test.go, cli_vsource_test.go license metadata and vSource attribution planted license keys appear nowhere; denied or empty license answers read as unavailable

Multi-vCenter vcsim integration

Tagged integration. Each loopback endpoint runs in its own cmd/vsfleet-vcsim process and can be killed mid-test. Fixtures are listed below.

Test Fixture Situation Asserts
TestVCSIMMultiContextInventoryAndProvenance topology two vCenters listed together every row carries its context and vCenter identity
TestVCSIMDuplicateNamesStayDistinct topology identical names in two vCenters rows never merge
TestVCSIMContextIsolation topology per-context credentials and sessions one context never uses another's session
TestVCSIMCLIJSONAndExitContract topology the JSON and exit-code contract across processes stable shapes and exit codes
TestVCSIMTopologyAncestryAndAttachments topology hierarchy and attachment queries exact ancestry and datastore/network attachments
TestVCSIMBlastRadiusNeverCrossesDuplicateNameContexts topology blast radius with colliding names impact never crosses into the other vCenter
TestVCSIMHistoryDiffVMHistoryAndCapacity history three captures with power and name changes between them diff, describe vm --history, and capacity follow the changes
TestVCSIMDiffTreatsLostContextAsUnknownNotRemoved history a context missing from the second capture its VMs are not covered, not removed
TestVCSIMPartialCoverageSurvivesProcessLoss partial-failure an endpoint process killed during an assessment the run is partial and the healthy context's data is kept
TestVCSIMPerfCollectRecordsProvenanceAndFailures basic-multivcenter capture perf with one endpoint taken down per-context status and provenance; values are not asserted because vcsim samples are random
TestVCSIMSizingCompleteAndPartialRuns basic-multivcenter destination sizing over complete and partial runs partial input is never reported as fit
TestVCSIMSharedExportPseudonymizesDuplicateNames duplicate-names a scoped, pseudonymized export colliding names stay distinguishable and originals never leak

Fixture catalogue

Deterministic topology flags create endpoints with independent vCenter and VM identities.

Fixture Topology
basic-multivcenter vc-prod: two datacenters, one cluster each, two hosts per cluster, three VMs per resource pool, three datastores, three port groups, one vApp per cluster. vc-edge: one datacenter/cluster, two hosts, one VM pool, datastore, port group, and vApp.
duplicate-names Identical one-datacenter estates with colliding VM and datastore names.
partial-failure Healthy and killable endpoints plus a closed-port context.
topology One datacenter/cluster, two hosts, VMs, datastores, and port groups, one vApp.
history Small basic estate, captured three times with power and name changes.

TUI scenarios

The harness sends keys and resizes to the TUI model and checks its view and semantic Observation. Golden scenarios compare renders at 60x20, 100x30, and 140x40 with internal/testbed/scenarios/testdata/golden/. See render contracts before updating goldens.

Scenario Profile Drives Asserts Goldens
overview presentation load all contexts with R inventory renders while the failed site stays visible yes
partial-failure presentation load all contexts the failed dr-site context is still shown no
duplicate-names presentation load all contexts same-named resources render as context-qualified rows no
credential-cancel presentation open the labelled prompt, then Esc prompt wraps; cancellation closes it and reports the result without fixture secrets yes
stale-result presentation initial load context selected only; reordered replies are covered by fuzz/unit tests no
history-coverage-gap presentation H History opens; goldens record the gap yes
add-context-no-secret connected start with in-memory keyring refs production backend selects a context; password-free saves are checked by TestContextopsSave* and TestFormPreservesANonInteractiveCredential, not this scenario no
credential-source-missing connected missing fixture env source, then diagnosis failure names VSFLEET_TESTBED_PASSWORD; diagnosis says set/restart or edit with e; provider never reads real environment yes
datastore-browser presentation open nvme-01 browser root /, datastore name, entries, and browser mode at each size yes
resize presentation resize through 60x20, 100x30, 140x40 inventory still renders and selection is kept no
vm-dashboard presentation pages 0–4, ranges 1h→30d→1h at each size bounded frames, whole page tabs, detail mode retained yes
vm-timeline presentation web-05 timeline, tabs 1–3 at each size bounded frames, tab marks, live events labelled with their vCenter, stored tab never labelled live yes
network-switches presentation both DVS-Production pages; DVS-Storage Wiring/vMotion bounded frames, page tabs, switch and port-group names yes
host-network presentation esxi-db-08 Network; move switch cursor; open DVS-Storage and return bounded frames/tabs, missing switch and unattached NICs shown, Esc returns to host yes
vlan-map presentation all-context VLAN map/where panel; pair compute-a on prod-vc and edge-vc bounded frames, both context columns, lost-connectivity VLANs named yes
cluster-workspace presentation compute-a Summary, Hosts & VMs (unfold host), Storage; compute-b bounded frames/tabs, missing datastore named, low HA reserve flagged yes

PTY journeys

Tagged linux && pty. Journeys run cmd/vsfleet-testbed in a pseudo-terminal with isolated home/XDG directories and loopback simulators. Assertions use normalized output; see PTY execution and artifacts.

Journey Situation Asserts
SSH failure restores the TUI SSH to a host whose ssh fails the failure is reported and the TUI is usable afterwards
SSH hang cancels and restores the TUI an ssh that never returns, cancelled with Ctrl-C the handoff ends as failed and the TUI comes back
cancel credential prompt and keep using UI Esc on the password prompt the cancellation is reported; help and navigation still work
browse datastore find recursively and navigate back datastore detail, browse, recursive *.vmdk find, open the result, walk back to the root each directory level and the detail pane come back in order
switch history panes and return without leaked state Tab through Changes, Trends, Runs, Health, then leave and reopen each pane header appears, and inventory state is unchanged after leaving
resize narrow to wide without corrupting state start at 60x20 in History, resize to 140x40, keep navigating panes keep working after the resize
VM dashboard pages, ranges, resize, and quit mid-query open a VM against the simulator, switch to the Disk page, step to 24h, resize 60x20→140x40, step to the unloaded 7d range and quit at once real performance queries render; the page and range survive both resizes; the process exits 0 with a query very likely still in flight
ctrl-c cancels active background work Ctrl-C while an assessment capture is running exit status 0

Send one key per write in a journey: Bubble Tea reads back-to-back runes as a single multi-rune key, so "rq" matches neither reload nor quit.

Fuzz targets

See Fuzzing for seeds, campaigns, and regression inputs.

Target Package Input Invariants
FuzzTUIKeyResizeNeverPanics internal/tui up to 256 arbitrary keys with arbitrary terminal sizes the model never panics
FuzzStaleMessagesCannotReplaceNewerState internal/tui pairs of load generations a reply from an older generation never paints over newer data
FuzzRenderIsBoundedAndDeterministic internal/tui terminal sizes rendering twice gives the same frame and no line is wider than the terminal
FuzzParseWorkbook internal/rvimport a block of arbitrary cells (header row included) written into one worksheet of a real vsfleet-written workbook Parse returns a result or an error, never a panic, and reports the same thing for the same workbook twice
FuzzLoadSaveRoundTrip internal/config arbitrary configuration text anything Load accepts survives Save and loads back to the same contexts
FuzzNormalizeThumbprintIsIdempotent internal/config arbitrary thumbprint text normalizing twice changes nothing
FuzzParseAndEvaluate internal/query arbitrary --where expressions with tags and custom attributes readable or not accepted unquoted predicates print back to an identical parse, tag./custom. always carry a name, and Evaluate never contradicts Match

Kubernetes end-to-end

The kind suite uses deploy/kubernetes/cronjob.yaml with the CI image and two vcsim Deployments, prod and edge.

Step Situation Asserts
security contract the CronJob as shipped non-root, read-only root filesystem, no privilege escalation, all capabilities dropped, fsGroup 65532
vsfleet-complete both vCenters up assessment status complete, 2 of 2 contexts
vsfleet-history capture list on the persistent volume the earlier run was persisted
vsfleet-partial edge scaled to zero, --fail-on-partial the job fails with exit code 3 and the result is partial, 1 of 2 contexts
vsfleet-readiness analyze readiness the verdict is not ready and unresolved items are listed

This tier checks ConfigMap mounts, Secret-backed file: credentials, PVC ownership, and Service DNS.

Release snapshot and pins

release-snapshot builds the full release without publishing or signing, checks every OS/architecture archive and WinGet manifest, runs the Linux binary and amd64 image, then scans them as described in vulnerability scanning.

python scripts/test-winget-publication.py checks the WinGet catalog comparison offline: an absent package, an older version, the released version, unrelated packages, and failed reads.

scripts/check-release-pins.sh fails when:

  • Dockerfile and Dockerfile.vcsim use different base images, or the base image is not pinned by digest;
  • the base.name/base.digest OCI annotations in .goreleaser.yaml stop reading BASE_IMAGE_NAME/BASE_IMAGE_DIGEST, which scripts/base-image-env.sh derives from the Dockerfile at release time (Dependabot bumps only the Dockerfiles);
  • any ghcr.io/easonliuuuuu/vsfleet:vX.Y.Z pin in README.md, docs/, or deploy/ differs from .release-please-manifest.json, or sits in a file that release-please does not bump;
  • a release-please extra-files entry has no x-release-please marker.

What nothing here proves

See validation limits and real-vCenter acceptance.

Keeping this page current

Add a row with each new scenario, journey, fuzz target, vcsim test, or CI job. TestCatalogueDocumentsEveryScenarioAndFuzzTarget in internal/testbed/scenarios enforces scenario/fuzz rows and fuzz workflow entries.