Pulse/internal/mock/platform_support_contract_test.go

1355 lines
43 KiB
Go

package mock
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"regexp"
"runtime"
"sort"
"strings"
"testing"
"time"
"github.com/rcourtman/pulse-go-rewrite/internal/platformsupport"
)
var (
numberedPlatformListRE = regexp.MustCompile("^\\d+\\.\\s+`([^`]+)`")
currentSupportMatrixRE = regexp.MustCompile("^\\|\\s+`([^`]+)`\\s+\\|")
currentSupportSetupRowRE = regexp.MustCompile("^\\|\\s+`([^`]+)`\\s+\\|\\s+([^|]+?)\\s+\\|")
currentSupportSurfaceRowRE = regexp.MustCompile("^\\|\\s+`([^`]+)`\\s+\\|\\s+`([^`]+)`\\s+\\|")
agentHostProfileTableRowRE = regexp.MustCompile("^\\|\\s+`([^`]+)`\\s+\\|\\s+`([^`]+)`\\s+\\|\\s+`([^`]+)`\\s+\\|\\s+`([^`]+)`\\s+\\|\\s+(.+?)\\s+\\|\\s+`([^`]+)`\\s+\\|\\s+`([^`]+)`\\s+\\|")
)
type platformSupportManifest struct {
SchemaVersion int `json:"schema_version"`
DefaultInfrastructureSourceOrder []string `json:"default_infrastructure_source_order"`
AgentHostProfiles []agentHostProfileManifestEntry `json:"agent_host_profiles"`
Platforms []platformSupportManifestEntry `json:"platforms"`
}
type agentHostProfileManifestEntry struct {
ID string `json:"id"`
Family string `json:"family"`
GovernanceState string `json:"governance_state"`
ReadinessStage string `json:"readiness_stage"`
HostIdentityTokens []string `json:"host_identity_tokens"`
RuntimePlatform string `json:"runtime_platform"`
SupportFloor map[string]string `json:"support_floor"`
StorageFamily string `json:"storage_family"`
}
type platformSupportManifestEntry struct {
ID string `json:"id"`
SurfaceKind string `json:"surface_kind"`
GovernanceState string `json:"governance_state"`
ReadinessStage string `json:"readiness_stage"`
PrimaryMode string `json:"primary_mode"`
OnboardingPaths []string `json:"onboarding_paths"`
CanonicalProjections []string `json:"canonical_projections"`
SupportFloor map[string]string `json:"support_floor"`
UILabel string `json:"ui_label"`
UITone string `json:"ui_tone"`
Aliases []string `json:"aliases"`
DisplayTokens []string `json:"display_tokens"`
StorageFamily string `json:"storage_family"`
}
func TestPlatformSupportManifestMatchesSupportModel(t *testing.T) {
model := loadPlatformSupportModel(t)
manifest := loadPlatformSupportManifest(t)
firstClassPlatforms := parsePlatformListSection(t, model, "### First-class platforms")
runtimeLenses := parsePlatformListSection(t, model, "### Runtime lenses")
supportedSurfaces := uniqueSortedPlatforms(append(
append([]string(nil), firstClassPlatforms...),
runtimeLenses...,
))
admitted := parsePlatformListSection(t, model, "### Admitted platforms (not yet supported)")
presentationOnly := parsePlatformListSection(t, model, "### Presentation-only platform vocabulary")
agentHostProfiles := parseAgentHostProfileSection(t, model, "### Agent Host Profiles")
matrix := parseCurrentSupportMatrixPlatforms(t, model)
if diff := diffPlatformSets(supportedSurfaces, matrix); diff != "" {
t.Fatalf("current supported platform definitions drifted between classification and support matrix:\n%s", diff)
}
if diff := diffPlatformSets(supportedSurfaces, manifestPlatformsByState(t, manifest, "supported")); diff != "" {
t.Fatalf("supported platform manifest drifted from the canonical support model:\n%s", diff)
}
if diff := diffPlatformSets(
firstClassPlatforms,
manifestPlatformsBySurfaceKindAndState(t, manifest, "platform", "supported"),
); diff != "" {
t.Fatalf("supported platform surface-kind manifest drifted from the canonical support model:\n%s", diff)
}
if diff := diffPlatformSets(
runtimeLenses,
manifestPlatformsBySurfaceKindAndState(t, manifest, "runtime-lens", "supported"),
); diff != "" {
t.Fatalf("supported runtime-lens manifest drifted from the canonical support model:\n%s", diff)
}
if diff := diffPlatformSets(admitted, manifestPlatformsByState(t, manifest, "admitted")); diff != "" {
t.Fatalf("admitted platform manifest drifted from the canonical support model:\n%s", diff)
}
if diff := diffPlatformSets(
presentationOnly,
manifestPlatformsByState(t, manifest, "presentation-only"),
); diff != "" {
t.Fatalf("presentation-only platform manifest drifted from the canonical support model:\n%s", diff)
}
if diff := diffPlatformSets(
presentationOnly,
manifestPlatformsBySurfaceKindAndState(t, manifest, "presentation-only", "presentation-only"),
); diff != "" {
t.Fatalf("presentation-only surface-kind manifest drifted from the canonical support model:\n%s", diff)
}
if diff := diffPlatformFieldMap(
parseCurrentSupportRowSurfaceKinds(t, model),
manifestPlatformSurfaceKindsByState(t, manifest, "supported"),
); diff != "" {
t.Fatalf("supported surface-kind manifest drifted from the canonical support rows:\n%s", diff)
}
if diff := diffPlatformFieldMap(
parseCurrentSupportMatrixOnboardingPaths(t, model),
manifestPlatformOnboardingPathsByState(t, manifest, "supported"),
); diff != "" {
t.Fatalf("supported onboarding-path manifest drifted from the canonical support model:\n%s", diff)
}
if diff := diffPlatformSets(
sortedAgentHostProfileIDs(agentHostProfiles),
manifestAgentHostProfileIDsByState(t, manifest, "supported"),
); diff != "" {
t.Fatalf("agent host profile manifest drifted from the canonical support model:\n%s", diff)
}
if diff := diffPlatformFieldMap(
agentHostProfileFieldMap(agentHostProfiles, func(profile agentHostProfileSectionEntry) []string {
return []string{profile.Family}
}),
manifestAgentHostProfileFamiliesByState(t, manifest, "supported"),
); diff != "" {
t.Fatalf("agent host profile family drifted from the canonical support model:\n%s", diff)
}
if diff := diffPlatformFieldMap(
agentHostProfileFieldMap(agentHostProfiles, func(profile agentHostProfileSectionEntry) []string {
return []string{profile.GovernanceState}
}),
manifestAgentHostProfileGovernanceStatesByState(t, manifest, "supported"),
); diff != "" {
t.Fatalf("agent host profile governance drifted from the canonical support model:\n%s", diff)
}
if diff := diffPlatformFieldMap(
agentHostProfileFieldMap(agentHostProfiles, func(profile agentHostProfileSectionEntry) []string {
return []string{profile.ReadinessStage}
}),
manifestAgentHostProfileReadinessStagesByState(t, manifest, "supported"),
); diff != "" {
t.Fatalf("agent host profile readiness drifted from the canonical support model:\n%s", diff)
}
if diff := diffPlatformFieldMap(
agentHostProfileFieldMap(agentHostProfiles, func(profile agentHostProfileSectionEntry) []string {
return append([]string(nil), profile.HostIdentityTokens...)
}),
manifestAgentHostProfileHostIdentityTokensByState(t, manifest, "supported"),
); diff != "" {
t.Fatalf("agent host profile host-identity tokens drifted from the canonical support model:\n%s", diff)
}
if diff := diffPlatformFieldMap(
agentHostProfileFieldMap(agentHostProfiles, func(profile agentHostProfileSectionEntry) []string {
return []string{profile.RuntimePlatform}
}),
manifestAgentHostProfileRuntimePlatformsByState(t, manifest, "supported"),
); diff != "" {
t.Fatalf("agent host profile runtime-platform fallback drifted from the canonical support model:\n%s", diff)
}
if diff := diffPlatformFieldMap(
agentHostProfileFieldMap(agentHostProfiles, func(profile agentHostProfileSectionEntry) []string {
return []string{profile.StorageFamily}
}),
manifestAgentHostProfileStorageFamiliesByState(t, manifest, "supported"),
); diff != "" {
t.Fatalf("agent host profile storage-family drifted from the canonical support model:\n%s", diff)
}
defaultInfrastructureSources := uniqueSortedPlatforms(append(
append([]string(nil), supportedSurfaces...),
admitted...,
))
if diff := diffPlatformSets(defaultInfrastructureSources, manifest.DefaultInfrastructureSourceOrder); diff != "" {
t.Fatalf("default infrastructure source ordering drifted from the canonical supported/admitted platform set:\n%s", diff)
}
}
func TestMockCoverageMatchesCurrentSupportedPlatformSet(t *testing.T) {
manifest := loadPlatformSupportManifest(t)
supported := manifestPlatformsByState(t, manifest, "supported")
checkers := map[string]func(*testing.T, FixtureGraph){
"agent": assertAgentMockCoverage,
"docker": assertDockerMockCoverage,
"kubernetes": assertKubernetesMockCoverage,
"proxmox-pbs": assertProxmoxPBSMockCoverage,
"proxmox-pmg": assertProxmoxPMGMockCoverage,
"proxmox-pve": assertProxmoxPVEMockCoverage,
"truenas": assertTrueNASMockCoverage,
}
if diff := diffPlatformSets(supported, sortedPlatformKeys(checkers)); diff != "" {
t.Fatalf("supported platform coverage guardrail drifted from the canonical support model:\n%s", diff)
}
graph := buildFixtureGraph(DefaultConfig, time.Date(2026, time.April, 10, 12, 0, 0, 0, time.UTC))
for _, platform := range supported {
checker := checkers[platform]
checker(t, graph)
}
}
func TestVMwareFixturesRemainAdmittedAtPhase1Floor(t *testing.T) {
model := loadPlatformSupportModel(t)
manifest := loadPlatformSupportManifest(t)
supported := manifestPlatformsByState(t, manifest, "supported")
admitted := manifestPlatformsByState(t, manifest, "admitted")
presentationOnly := manifestPlatformsByState(t, manifest, "presentation-only")
if containsPlatform(supported, "vmware-vsphere") {
t.Fatal("vmware-vsphere must not appear in the current supported platform set before live vCenter proof")
}
if !containsPlatform(admitted, "vmware-vsphere") {
t.Fatal("vmware-vsphere must remain in the admitted set until live vCenter proof promotes it")
}
if containsPlatform(presentationOnly, "vmware-vsphere") {
t.Fatal("vmware-vsphere must not regress into presentation-only vocabulary while admitted")
}
if !strings.Contains(model, "| `vmware-vsphere` | VMware | `vCenter` only in phase 1 |") {
t.Fatal("expected platform support model to keep the vmware-vsphere admission row")
}
vmwareOnboardingPaths, ok := manifestPlatformOnboardingPaths(manifest)["vmware-vsphere"]
if !ok {
t.Fatal("expected vmware-vsphere onboarding-path manifest entry")
}
if diff := diffPlatformSets([]string{"platform-connections"}, vmwareOnboardingPaths); diff != "" {
t.Fatalf("vmware-vsphere onboarding path drifted from the support model:\n%s", diff)
}
vmwareManifest := requireManifestPlatform(t, manifest, "vmware-vsphere")
if vmwareManifest.GovernanceState != "admitted" {
t.Fatalf("vmware governance state = %q, want admitted", vmwareManifest.GovernanceState)
}
if vmwareManifest.ReadinessStage != "first-lab-ready" {
t.Fatalf("vmware readiness stage = %q, want first-lab-ready", vmwareManifest.ReadinessStage)
}
if vmwareManifest.PrimaryMode != "api-backed" {
t.Fatalf("vmware primary mode = %q, want api-backed", vmwareManifest.PrimaryMode)
}
if diff := diffPlatformSets([]string{"agent", "network", "storage", "vm"}, vmwareManifest.CanonicalProjections); diff != "" {
t.Fatalf("vmware canonical projections drifted from the support model:\n%s", diff)
}
if got := vmwareManifest.SupportFloor["recovery"]; got != "n/a" {
t.Fatalf("vmware recovery support floor = %q, want n/a", got)
}
if got := vmwareManifest.SupportFloor["assistant_control"]; got != "read-only" {
t.Fatalf("vmware assistant control support floor = %q, want read-only", got)
}
graph := buildFixtureGraph(DefaultConfig, time.Date(2026, time.April, 10, 12, 0, 0, 0, time.UTC))
assertVMwareMockCoverage(t, graph)
fixture := DefaultVMwareConnectionFixture()
if fixture.CollectedAt.IsZero() {
t.Fatal("expected VMware mock connection fixture freshness")
}
if fixture.Hosts == 0 || fixture.VMs == 0 || fixture.Datastores == 0 || fixture.Networks == 0 {
t.Fatalf("expected VMware mock connection fixture to describe canonical inventory, got %+v", fixture)
}
}
func TestUnraidRemainsInPresentationOnlyVocabularyAndAgentHostProfiles(t *testing.T) {
model := loadPlatformSupportModel(t)
manifest := loadPlatformSupportManifest(t)
supported := manifestPlatformsByState(t, manifest, "supported")
admitted := manifestPlatformsByState(t, manifest, "admitted")
presentationOnly := manifestPlatformsByState(t, manifest, "presentation-only")
profiles := parseAgentHostProfileSection(t, model, "### Agent Host Profiles")
if containsPlatform(supported, "unraid") {
t.Fatal("unraid must not appear in the current supported platform set")
}
if containsPlatform(admitted, "unraid") {
t.Fatal("unraid must not appear in the admitted platform set")
}
if !containsPlatform(presentationOnly, "unraid") {
t.Fatal("expected unraid to remain presentation-only platform vocabulary")
}
if !containsPlatform(sortedAgentHostProfileIDs(profiles), "unraid") {
t.Fatal("expected unraid to remain a supported agent host profile")
}
profile := requireAgentHostProfileManifestEntry(t, manifest, "unraid")
if profile.GovernanceState != "supported" {
t.Fatalf("unraid governance state = %q, want supported", profile.GovernanceState)
}
if profile.ReadinessStage != "supported" {
t.Fatalf("unraid readiness stage = %q, want supported", profile.ReadinessStage)
}
if diff := diffPlatformSets([]string{"unraid", "unraid-os", "unraid os"}, profile.HostIdentityTokens); diff != "" {
t.Fatalf("unraid host identity tokens drifted from the host-profile model:\n%s", diff)
}
if profile.RuntimePlatform != "linux" {
t.Fatalf("unraid runtime platform = %q, want linux", profile.RuntimePlatform)
}
if profile.StorageFamily != "onprem" {
t.Fatalf("unraid storage family = %q, want onprem", profile.StorageFamily)
}
if got := profile.SupportFloor["assistant_control"]; got != "read-only" {
t.Fatalf("unraid assistant control support floor = %q, want read-only", got)
}
if got := profile.SupportFloor["storage"]; got != "supported" {
t.Fatalf("unraid storage support floor = %q, want supported", got)
}
if !strings.Contains(model, "### Agent Host Profiles") {
t.Fatal("expected platform support model to keep an agent host profiles section")
}
if !strings.Contains(
model,
"| `unraid` | `Unraid` | `supported` | `supported` | `unraid`, `unraid-os`, `unraid os` | `linux` | `onprem` |",
) {
t.Fatal("expected platform support model to keep the unraid host-profile row")
}
}
func TestPlatformSupportBackendProjectionMatchesSupportManifest(t *testing.T) {
manifest := loadPlatformSupportManifest(t)
profiles := platformsupport.AgentHostProfiles()
gotIDs := make([]string, 0, len(profiles))
gotTokens := make(map[string][]string, len(profiles))
gotRuntimePlatforms := make(map[string][]string, len(profiles))
for _, profile := range profiles {
if profile.GovernanceState != "supported" {
continue
}
gotIDs = append(gotIDs, profile.ID)
gotTokens[profile.ID] = append([]string(nil), profile.HostIdentityTokens...)
gotRuntimePlatforms[profile.ID] = []string{profile.RuntimePlatform}
}
if diff := diffPlatformSets(manifestAgentHostProfileIDsByState(t, manifest, "supported"), gotIDs); diff != "" {
t.Fatalf("backend agent host profile ids drifted from manifest:\n%s", diff)
}
if diff := diffPlatformFieldMap(
manifestAgentHostProfileHostIdentityTokensByState(t, manifest, "supported"),
gotTokens,
); diff != "" {
t.Fatalf("backend agent host profile identity tokens drifted from manifest:\n%s", diff)
}
if diff := diffPlatformFieldMap(
manifestAgentHostProfileRuntimePlatformsByState(t, manifest, "supported"),
gotRuntimePlatforms,
); diff != "" {
t.Fatalf("backend agent host profile runtime platforms drifted from manifest:\n%s", diff)
}
profile, ok := platformsupport.AgentHostProfileForIdentity("Unraid")
if !ok {
t.Fatal("expected backend projection to resolve Unraid identity token")
}
if profile.ID != "unraid" || profile.RuntimePlatform != "linux" {
t.Fatalf("backend Unraid profile = %+v, want id=unraid runtime=linux", profile)
}
if got := platformsupport.NormalizeRuntimePlatformForAgentHostProfile("unraid", "unraid"); got != "linux" {
t.Fatalf("backend runtime normalization = %q, want linux", got)
}
}
func assertAgentMockCoverage(t *testing.T, graph FixtureGraph) {
t.Helper()
if len(graph.State.Hosts) == 0 {
t.Fatal("expected host-agent fixtures for the supported agent platform")
}
if len(graph.State.PhysicalDisks) == 0 {
t.Fatal("expected physical-disk fixtures for the supported agent platform")
}
}
func assertDockerMockCoverage(t *testing.T, graph FixtureGraph) {
t.Helper()
if len(graph.State.DockerHosts) == 0 {
t.Fatal("expected docker host fixtures for the supported docker platform")
}
containerCount := 0
imageCount := 0
volumeCount := 0
networkCount := 0
storageUsageCount := 0
swarmNodeCount := 0
for _, host := range graph.State.DockerHosts {
containerCount += len(host.Containers)
imageCount += len(host.Images)
volumeCount += len(host.Volumes)
networkCount += len(host.Networks)
swarmNodeCount += len(host.Nodes)
if host.StorageUsage != nil {
storageUsageCount++
}
}
if containerCount == 0 {
t.Fatal("expected docker container fixtures for the supported docker platform")
}
if imageCount == 0 || volumeCount == 0 || networkCount == 0 || storageUsageCount == 0 || swarmNodeCount == 0 {
t.Fatalf(
"expected docker fixtures to include images, volumes, networks, storage usage, and swarm nodes; got images=%d volumes=%d networks=%d storage=%d swarmNodes=%d",
imageCount,
volumeCount,
networkCount,
storageUsageCount,
swarmNodeCount,
)
}
}
func assertKubernetesMockCoverage(t *testing.T, graph FixtureGraph) {
t.Helper()
if len(graph.State.KubernetesClusters) == 0 {
t.Fatal("expected kubernetes cluster fixtures for the supported kubernetes platform")
}
nodeCount := 0
deploymentCount := 0
podCount := 0
statefulSetCount := 0
daemonSetCount := 0
jobCount := 0
cronJobCount := 0
serviceCount := 0
ingressCount := 0
endpointSliceCount := 0
storageClassCount := 0
persistentVolumeCount := 0
persistentVolumeClaimCount := 0
configMapCount := 0
secretCount := 0
serviceAccountCount := 0
networkPolicyCount := 0
resourceQuotaCount := 0
limitRangeCount := 0
podDisruptionBudgetCount := 0
horizontalPodAutoscalerCount := 0
eventCount := 0
for _, cluster := range graph.State.KubernetesClusters {
nodeCount += len(cluster.Nodes)
deploymentCount += len(cluster.Deployments)
podCount += len(cluster.Pods)
statefulSetCount += len(cluster.StatefulSets)
daemonSetCount += len(cluster.DaemonSets)
jobCount += len(cluster.Jobs)
cronJobCount += len(cluster.CronJobs)
serviceCount += len(cluster.Services)
ingressCount += len(cluster.Ingresses)
endpointSliceCount += len(cluster.EndpointSlices)
storageClassCount += len(cluster.StorageClasses)
persistentVolumeCount += len(cluster.PersistentVolumes)
persistentVolumeClaimCount += len(cluster.PersistentVolumeClaims)
configMapCount += len(cluster.ConfigMaps)
secretCount += len(cluster.Secrets)
serviceAccountCount += len(cluster.ServiceAccounts)
networkPolicyCount += len(cluster.NetworkPolicies)
resourceQuotaCount += len(cluster.ResourceQuotas)
limitRangeCount += len(cluster.LimitRanges)
podDisruptionBudgetCount += len(cluster.PodDisruptionBudgets)
horizontalPodAutoscalerCount += len(cluster.HorizontalPodAutoscalers)
eventCount += len(cluster.Events)
}
if nodeCount == 0 || deploymentCount == 0 || podCount == 0 {
t.Fatalf(
"expected kubernetes fixtures to include nodes, deployments, and pods; got nodes=%d deployments=%d pods=%d",
nodeCount,
deploymentCount,
podCount,
)
}
if statefulSetCount == 0 || daemonSetCount == 0 || jobCount == 0 || cronJobCount == 0 {
t.Fatalf(
"expected kubernetes fixtures to include workload controllers; got statefulSets=%d daemonSets=%d jobs=%d cronJobs=%d",
statefulSetCount,
daemonSetCount,
jobCount,
cronJobCount,
)
}
if serviceCount == 0 || ingressCount == 0 || endpointSliceCount == 0 {
t.Fatalf(
"expected kubernetes fixtures to include services, ingresses, and endpoint slices; got services=%d ingresses=%d endpointSlices=%d",
serviceCount,
ingressCount,
endpointSliceCount,
)
}
if storageClassCount == 0 || persistentVolumeCount == 0 || persistentVolumeClaimCount == 0 {
t.Fatalf(
"expected kubernetes fixtures to include storage classes, persistent volumes, and persistent volume claims; got classes=%d pvs=%d pvcs=%d",
storageClassCount,
persistentVolumeCount,
persistentVolumeClaimCount,
)
}
if configMapCount == 0 || secretCount == 0 || serviceAccountCount == 0 {
t.Fatalf(
"expected kubernetes fixtures to include configuration resources; got configMaps=%d secrets=%d serviceAccounts=%d",
configMapCount,
secretCount,
serviceAccountCount,
)
}
if networkPolicyCount == 0 || resourceQuotaCount == 0 || limitRangeCount == 0 || podDisruptionBudgetCount == 0 {
t.Fatalf(
"expected kubernetes fixtures to include policy resources; got networkPolicies=%d resourceQuotas=%d limitRanges=%d pdbs=%d",
networkPolicyCount,
resourceQuotaCount,
limitRangeCount,
podDisruptionBudgetCount,
)
}
if horizontalPodAutoscalerCount == 0 || eventCount == 0 {
t.Fatalf(
"expected kubernetes fixtures to include autoscaling and event resources; got hpas=%d events=%d",
horizontalPodAutoscalerCount,
eventCount,
)
}
}
func assertProxmoxPVEMockCoverage(t *testing.T, graph FixtureGraph) {
t.Helper()
if len(graph.State.Nodes) == 0 {
t.Fatal("expected proxmox-pve node fixtures")
}
if len(graph.State.VMs) == 0 {
t.Fatal("expected proxmox-pve VM fixtures")
}
if len(graph.State.Containers) == 0 {
t.Fatal("expected proxmox-pve system-container fixtures")
}
if len(graph.State.Storage) == 0 {
t.Fatal("expected proxmox-pve storage fixtures")
}
if len(graph.State.PhysicalDisks) == 0 {
t.Fatal("expected proxmox-pve physical-disk fixtures")
}
if len(graph.State.PVEBackups.StorageBackups) == 0 {
t.Fatal("expected proxmox-pve recovery fixtures")
}
}
func assertProxmoxPBSMockCoverage(t *testing.T, graph FixtureGraph) {
t.Helper()
if len(graph.State.PBSInstances) == 0 {
t.Fatal("expected proxmox-pbs instance fixtures")
}
if len(graph.State.PBSBackups) == 0 {
t.Fatal("expected proxmox-pbs backup fixtures")
}
}
func assertProxmoxPMGMockCoverage(t *testing.T, graph FixtureGraph) {
t.Helper()
if len(graph.State.PMGInstances) == 0 {
t.Fatal("expected proxmox-pmg instance fixtures")
}
if len(graph.State.PMGBackups) == 0 {
t.Fatal("expected proxmox-pmg backup fixtures")
}
}
func assertTrueNASMockCoverage(t *testing.T, graph FixtureGraph) {
t.Helper()
snapshot := graph.PlatformFixtures.TrueNAS
if strings.TrimSpace(snapshot.System.Hostname) == "" {
t.Fatal("expected truenas system fixture")
}
if len(snapshot.Apps) == 0 {
t.Fatal("expected truenas app fixtures")
}
if len(snapshot.Pools) == 0 || len(snapshot.Datasets) == 0 {
t.Fatal("expected truenas storage fixtures")
}
if len(snapshot.Disks) == 0 {
t.Fatal("expected truenas disk fixtures")
}
if len(snapshot.ZFSSnapshots)+len(snapshot.ReplicationTasks) == 0 {
t.Fatal("expected truenas recovery fixtures")
}
fixture := DefaultTrueNASConnectionFixture()
if fixture.CollectedAt.IsZero() {
t.Fatal("expected truenas mock connection fixture freshness")
}
if fixture.Systems == 0 || fixture.StoragePools == 0 || fixture.Apps == 0 || fixture.Disks == 0 {
t.Fatalf("expected truenas mock connection fixture to describe canonical inventory, got %+v", fixture)
}
}
func assertVMwareMockCoverage(t *testing.T, graph FixtureGraph) {
t.Helper()
if len(graph.PlatformFixtures.VMware.Hosts) == 0 {
t.Fatal("expected VMware mock fixtures to include hosts")
}
if len(graph.PlatformFixtures.VMware.VMs) == 0 {
t.Fatal("expected VMware mock fixtures to include VMs")
}
if len(graph.PlatformFixtures.VMware.Datastores) == 0 {
t.Fatal("expected VMware mock fixtures to include datastores")
}
}
func loadPlatformSupportManifest(t *testing.T) platformSupportManifest {
t.Helper()
_, currentFile, _, ok := runtime.Caller(0)
if !ok {
t.Fatal("failed to locate current test file for platform support manifest lookup")
}
manifestPath := filepath.Join(
filepath.Dir(currentFile),
"..",
"..",
"docs",
"release-control",
"v6",
"internal",
"PLATFORM_SUPPORT_MANIFEST.json",
)
manifestBytes, err := os.ReadFile(manifestPath)
if err != nil {
t.Fatalf("read platform support manifest: %v", err)
}
var manifest platformSupportManifest
if err := json.Unmarshal(manifestBytes, &manifest); err != nil {
t.Fatalf("unmarshal platform support manifest: %v", err)
}
if manifest.SchemaVersion < 1 {
t.Fatalf("expected positive platform support manifest schema version, got %d", manifest.SchemaVersion)
}
if len(manifest.Platforms) == 0 {
t.Fatal("expected platform support manifest to declare at least one platform")
}
if len(manifest.DefaultInfrastructureSourceOrder) == 0 {
t.Fatal("expected platform support manifest to declare a default infrastructure source order")
}
return manifest
}
func loadPlatformSupportModel(t *testing.T) string {
t.Helper()
_, currentFile, _, ok := runtime.Caller(0)
if !ok {
t.Fatal("failed to locate current test file for platform support model lookup")
}
modelPath := filepath.Join(
filepath.Dir(currentFile),
"..",
"..",
"docs",
"release-control",
"v6",
"internal",
"PLATFORM_SUPPORT_MODEL.md",
)
modelBytes, err := os.ReadFile(modelPath)
if err != nil {
t.Fatalf("read platform support model: %v", err)
}
return string(modelBytes)
}
func parsePlatformListSection(t *testing.T, model string, heading string) []string {
t.Helper()
var platforms []string
inSection := false
// The admitted-platform section is allowed to be empty once the last
// admitted platform graduates to supported; every other section must
// declare at least one platform.
allowEmpty := strings.Contains(heading, "Admitted platforms")
for _, raw := range strings.Split(model, "\n") {
line := strings.TrimSpace(raw)
switch {
case line == heading:
inSection = true
continue
case !inSection:
continue
case strings.HasPrefix(line, "### ") || strings.HasPrefix(line, "## "):
if allowEmpty {
return allowEmptyPlatformList(platforms)
}
return requireNonEmptyPlatformList(t, platforms, heading)
}
matches := numberedPlatformListRE.FindStringSubmatch(line)
if len(matches) == 2 {
platforms = append(platforms, matches[1])
}
}
if allowEmpty {
return allowEmptyPlatformList(platforms)
}
return requireNonEmptyPlatformList(t, platforms, heading)
}
func parseCurrentSupportMatrixPlatforms(t *testing.T, model string) []string {
t.Helper()
var platforms []string
inSection := false
for _, raw := range strings.Split(model, "\n") {
line := strings.TrimSpace(raw)
switch {
case line == "## Current Support Matrix":
inSection = true
continue
case !inSection:
continue
case strings.HasPrefix(line, "## "):
return requireNonEmptyPlatformList(t, platforms, "current support matrix")
}
matches := currentSupportMatrixRE.FindStringSubmatch(line)
if len(matches) == 2 {
platforms = append(platforms, matches[1])
}
}
return requireNonEmptyPlatformList(t, platforms, "current support matrix")
}
func parseCurrentSupportMatrixOnboardingPaths(t *testing.T, model string) map[string][]string {
t.Helper()
rows := make(map[string][]string)
inSetupTable := false
for _, raw := range strings.Split(model, "\n") {
line := strings.TrimSpace(raw)
switch {
case strings.HasPrefix(line, "| Platform") && strings.Contains(line, "| Setup"):
inSetupTable = true
continue
case !inSetupTable:
continue
case strings.HasPrefix(line, "## "):
return requireNonEmptyPlatformFieldMap(t, rows, "current support matrix onboarding paths")
case line == "" || strings.HasPrefix(line, "| ---"):
continue
}
matches := currentSupportSetupRowRE.FindStringSubmatch(line)
if len(matches) != 3 {
continue
}
rows[matches[1]] = parseSetupCellOnboardingPaths(t, matches[2])
}
return requireNonEmptyPlatformFieldMap(t, rows, "current support matrix onboarding paths")
}
func parseCurrentSupportRowSurfaceKinds(t *testing.T, model string) map[string][]string {
t.Helper()
rows := make(map[string][]string)
inRows := false
for _, raw := range strings.Split(model, "\n") {
line := strings.TrimSpace(raw)
switch {
case line == "### Current support rows":
inRows = true
continue
case !inRows:
continue
case strings.HasPrefix(line, "### ") || strings.HasPrefix(line, "## "):
return requireNonEmptyPlatformFieldMap(t, rows, "current support row surface kinds")
case line == "" || strings.HasPrefix(line, "| ---") || strings.HasPrefix(line, "| Platform"):
continue
}
matches := currentSupportSurfaceRowRE.FindStringSubmatch(line)
if len(matches) != 3 {
continue
}
rows[matches[1]] = []string{matches[2]}
}
return requireNonEmptyPlatformFieldMap(t, rows, "current support row surface kinds")
}
func manifestPlatformsByState(
t *testing.T,
manifest platformSupportManifest,
governanceState string,
) []string {
t.Helper()
platforms := make([]string, 0)
for _, platform := range manifest.Platforms {
if strings.TrimSpace(platform.ID) == "" {
t.Fatal("expected platform support manifest platform ids to be non-empty")
}
if strings.TrimSpace(platform.GovernanceState) == "" {
t.Fatalf("expected platform support manifest to classify %s", platform.ID)
}
if platform.GovernanceState == governanceState {
platforms = append(platforms, platform.ID)
}
}
if governanceState == "admitted" {
return allowEmptyPlatformList(platforms)
}
return requireNonEmptyPlatformList(t, platforms, fmt.Sprintf("manifest platforms with state %s", governanceState))
}
func manifestPlatformsBySurfaceKindAndState(
t *testing.T,
manifest platformSupportManifest,
surfaceKind string,
governanceState string,
) []string {
t.Helper()
platforms := make([]string, 0)
for _, platform := range manifest.Platforms {
if platform.GovernanceState == governanceState && platform.SurfaceKind == surfaceKind {
platforms = append(platforms, platform.ID)
}
}
if governanceState == "admitted" {
return allowEmptyPlatformList(platforms)
}
return requireNonEmptyPlatformList(
t,
platforms,
fmt.Sprintf("manifest platforms with surface kind %s and state %s", surfaceKind, governanceState),
)
}
func manifestPlatformSurfaceKindsByState(
t *testing.T,
manifest platformSupportManifest,
governanceState string,
) map[string][]string {
t.Helper()
rows := make(map[string][]string)
for _, platform := range manifest.Platforms {
if platform.GovernanceState != governanceState {
continue
}
if strings.TrimSpace(platform.SurfaceKind) == "" {
t.Fatalf("expected platform support manifest to classify surface kind for %s", platform.ID)
}
rows[platform.ID] = []string{platform.SurfaceKind}
}
return requireNonEmptyPlatformFieldMap(
t,
rows,
fmt.Sprintf("manifest surface kinds with state %s", governanceState),
)
}
func manifestPlatformOnboardingPathsByState(
t *testing.T,
manifest platformSupportManifest,
governanceState string,
) map[string][]string {
t.Helper()
rows := make(map[string][]string)
for _, platform := range manifest.Platforms {
if platform.GovernanceState != governanceState {
continue
}
rows[platform.ID] = validateManifestOnboardingPaths(t, platform)
}
return requireNonEmptyPlatformFieldMap(
t,
rows,
fmt.Sprintf("manifest onboarding paths with state %s", governanceState),
)
}
func manifestPlatformOnboardingPaths(manifest platformSupportManifest) map[string][]string {
rows := make(map[string][]string, len(manifest.Platforms))
for _, platform := range manifest.Platforms {
rows[platform.ID] = append([]string(nil), platform.OnboardingPaths...)
}
return rows
}
type agentHostProfileSectionEntry struct {
Family string
GovernanceState string
ReadinessStage string
HostIdentityTokens []string
RuntimePlatform string
StorageFamily string
}
func parseAgentHostProfileSection(
t *testing.T,
model string,
heading string,
) map[string]agentHostProfileSectionEntry {
t.Helper()
entries := make(map[string]agentHostProfileSectionEntry)
inSection := false
for _, raw := range strings.Split(model, "\n") {
line := strings.TrimSpace(raw)
switch {
case line == heading:
inSection = true
continue
case !inSection:
continue
case strings.HasPrefix(line, "### ") || strings.HasPrefix(line, "## "):
return requireNonEmptyAgentHostProfileMap(t, entries, heading)
case line == "" || strings.HasPrefix(line, "| ---"):
continue
}
matches := agentHostProfileTableRowRE.FindStringSubmatch(line)
if len(matches) != 8 {
continue
}
entries[matches[1]] = agentHostProfileSectionEntry{
Family: matches[2],
GovernanceState: matches[3],
ReadinessStage: matches[4],
HostIdentityTokens: parseInlineTokenList(matches[5]),
RuntimePlatform: matches[6],
StorageFamily: matches[7],
}
}
return requireNonEmptyAgentHostProfileMap(t, entries, heading)
}
func parseInlineTokenList(value string) []string {
tokens := make([]string, 0, 1)
for _, token := range strings.Split(value, ",") {
token = strings.TrimSpace(token)
token = strings.Trim(token, "`")
if token == "" {
continue
}
tokens = append(tokens, token)
}
return tokens
}
func sortedAgentHostProfileIDs(profiles map[string]agentHostProfileSectionEntry) []string {
ids := make([]string, 0, len(profiles))
for id := range profiles {
ids = append(ids, id)
}
sort.Strings(ids)
return ids
}
func agentHostProfileFieldMap(
profiles map[string]agentHostProfileSectionEntry,
selector func(agentHostProfileSectionEntry) []string,
) map[string][]string {
rows := make(map[string][]string, len(profiles))
for id, profile := range profiles {
rows[id] = append([]string(nil), selector(profile)...)
}
return rows
}
func requireNonEmptyAgentHostProfileMap(
t *testing.T,
entries map[string]agentHostProfileSectionEntry,
heading string,
) map[string]agentHostProfileSectionEntry {
t.Helper()
if len(entries) == 0 {
t.Fatalf("expected %s to declare at least one agent host profile", heading)
}
return entries
}
func manifestAgentHostProfileIDsByState(
t *testing.T,
manifest platformSupportManifest,
governanceState string,
) []string {
t.Helper()
ids := make([]string, 0)
for _, profile := range manifest.AgentHostProfiles {
if strings.TrimSpace(profile.ID) == "" {
t.Fatal("expected agent host profile ids to be non-empty")
}
if strings.TrimSpace(profile.GovernanceState) == "" {
t.Fatalf("expected agent host profile manifest to classify %s", profile.ID)
}
if profile.GovernanceState == governanceState {
ids = append(ids, profile.ID)
}
}
return requireNonEmptyPlatformList(
t,
ids,
fmt.Sprintf("manifest agent host profiles with state %s", governanceState),
)
}
func manifestAgentHostProfileFieldMap(
t *testing.T,
manifest platformSupportManifest,
governanceState string,
selector func(agentHostProfileManifestEntry) []string,
label string,
) map[string][]string {
t.Helper()
rows := make(map[string][]string)
for _, profile := range manifest.AgentHostProfiles {
if profile.GovernanceState != governanceState {
continue
}
rows[profile.ID] = append([]string(nil), selector(profile)...)
}
return requireNonEmptyPlatformFieldMap(t, rows, label)
}
func manifestAgentHostProfileFamiliesByState(
t *testing.T,
manifest platformSupportManifest,
governanceState string,
) map[string][]string {
return manifestAgentHostProfileFieldMap(
t,
manifest,
governanceState,
func(profile agentHostProfileManifestEntry) []string { return []string{profile.Family} },
fmt.Sprintf("manifest agent host profile families with state %s", governanceState),
)
}
func manifestAgentHostProfileGovernanceStatesByState(
t *testing.T,
manifest platformSupportManifest,
governanceState string,
) map[string][]string {
return manifestAgentHostProfileFieldMap(
t,
manifest,
governanceState,
func(profile agentHostProfileManifestEntry) []string { return []string{profile.GovernanceState} },
fmt.Sprintf("manifest agent host profile governance with state %s", governanceState),
)
}
func manifestAgentHostProfileReadinessStagesByState(
t *testing.T,
manifest platformSupportManifest,
governanceState string,
) map[string][]string {
return manifestAgentHostProfileFieldMap(
t,
manifest,
governanceState,
func(profile agentHostProfileManifestEntry) []string { return []string{profile.ReadinessStage} },
fmt.Sprintf("manifest agent host profile readiness with state %s", governanceState),
)
}
func manifestAgentHostProfileHostIdentityTokensByState(
t *testing.T,
manifest platformSupportManifest,
governanceState string,
) map[string][]string {
return manifestAgentHostProfileFieldMap(
t,
manifest,
governanceState,
func(profile agentHostProfileManifestEntry) []string {
return append([]string(nil), profile.HostIdentityTokens...)
},
fmt.Sprintf("manifest agent host profile identity tokens with state %s", governanceState),
)
}
func manifestAgentHostProfileRuntimePlatformsByState(
t *testing.T,
manifest platformSupportManifest,
governanceState string,
) map[string][]string {
return manifestAgentHostProfileFieldMap(
t,
manifest,
governanceState,
func(profile agentHostProfileManifestEntry) []string { return []string{profile.RuntimePlatform} },
fmt.Sprintf("manifest agent host profile runtime platforms with state %s", governanceState),
)
}
func manifestAgentHostProfileStorageFamiliesByState(
t *testing.T,
manifest platformSupportManifest,
governanceState string,
) map[string][]string {
return manifestAgentHostProfileFieldMap(
t,
manifest,
governanceState,
func(profile agentHostProfileManifestEntry) []string { return []string{profile.StorageFamily} },
fmt.Sprintf("manifest agent host profile storage families with state %s", governanceState),
)
}
func requireAgentHostProfileManifestEntry(
t *testing.T,
manifest platformSupportManifest,
profileID string,
) agentHostProfileManifestEntry {
t.Helper()
for _, profile := range manifest.AgentHostProfiles {
if profile.ID == profileID {
return profile
}
}
t.Fatalf("expected agent host profile manifest entry for %s", profileID)
return agentHostProfileManifestEntry{}
}
func requireManifestPlatform(t *testing.T, manifest platformSupportManifest, platformID string) platformSupportManifestEntry {
t.Helper()
for _, platform := range manifest.Platforms {
if platform.ID == platformID {
return platform
}
}
t.Fatalf("expected platform support manifest entry for %s", platformID)
return platformSupportManifestEntry{}
}
func requireNonEmptyPlatformList(t *testing.T, platforms []string, label string) []string {
t.Helper()
unique := uniqueSortedPlatforms(platforms)
if len(unique) == 0 {
t.Fatalf("expected %s to declare at least one platform", label)
}
return unique
}
// allowEmptyPlatformList mirrors requireNonEmptyPlatformList but tolerates an
// empty result. Used for governance sets that can legitimately be empty
// (e.g. the admitted-platform staging area after the last admitted platform
// has been promoted to supported).
func allowEmptyPlatformList(platforms []string) []string {
return uniqueSortedPlatforms(platforms)
}
func requireNonEmptyPlatformFieldMap(
t *testing.T,
values map[string][]string,
label string,
) map[string][]string {
t.Helper()
if len(values) == 0 {
t.Fatalf("expected %s to declare at least one platform", label)
}
return values
}
func uniqueSortedPlatforms(platforms []string) []string {
seen := make(map[string]struct{}, len(platforms))
for _, platform := range platforms {
platform = strings.TrimSpace(platform)
if platform == "" {
continue
}
seen[platform] = struct{}{}
}
out := make([]string, 0, len(seen))
for platform := range seen {
out = append(out, platform)
}
sort.Strings(out)
return out
}
func sortedPlatformKeys(checkers map[string]func(*testing.T, FixtureGraph)) []string {
keys := make([]string, 0, len(checkers))
for key := range checkers {
keys = append(keys, key)
}
sort.Strings(keys)
return keys
}
func containsPlatform(platforms []string, want string) bool {
for _, platform := range platforms {
if platform == want {
return true
}
}
return false
}
func diffPlatformSets(want []string, got []string) string {
wantSet := make(map[string]struct{}, len(want))
for _, platform := range want {
wantSet[platform] = struct{}{}
}
gotSet := make(map[string]struct{}, len(got))
for _, platform := range got {
gotSet[platform] = struct{}{}
}
missing := make([]string, 0)
for _, platform := range want {
if _, ok := gotSet[platform]; !ok {
missing = append(missing, platform)
}
}
extra := make([]string, 0)
for _, platform := range got {
if _, ok := wantSet[platform]; !ok {
extra = append(extra, platform)
}
}
if len(missing) == 0 && len(extra) == 0 {
return ""
}
parts := make([]string, 0, 2)
if len(missing) > 0 {
parts = append(parts, fmt.Sprintf("missing: %s", strings.Join(missing, ", ")))
}
if len(extra) > 0 {
parts = append(parts, fmt.Sprintf("extra: %s", strings.Join(extra, ", ")))
}
return strings.Join(parts, "\n")
}
func diffPlatformFieldMap(want map[string][]string, got map[string][]string) string {
platforms := make(map[string]struct{}, len(want)+len(got))
for platform := range want {
platforms[platform] = struct{}{}
}
for platform := range got {
platforms[platform] = struct{}{}
}
keys := make([]string, 0, len(platforms))
for platform := range platforms {
keys = append(keys, platform)
}
sort.Strings(keys)
messages := make([]string, 0)
for _, platform := range keys {
wantValue, wantOK := want[platform]
gotValue, gotOK := got[platform]
switch {
case !wantOK:
messages = append(messages, fmt.Sprintf("unexpected platform %s", platform))
case !gotOK:
messages = append(messages, fmt.Sprintf("missing platform %s", platform))
default:
if diff := diffPlatformSets(wantValue, gotValue); diff != "" {
messages = append(
messages,
fmt.Sprintf("%s field values drifted:\n%s", platform, indentDiff(diff)),
)
}
}
}
return strings.Join(messages, "\n")
}
func parseSetupCellOnboardingPaths(t *testing.T, value string) []string {
t.Helper()
normalized := strings.ToLower(strings.TrimSpace(value))
paths := make([]string, 0, 2)
if strings.Contains(normalized, "install workspace") {
paths = append(paths, "install-workspace")
}
if strings.Contains(normalized, "platform connections") {
paths = append(paths, "platform-connections")
}
return requireNonEmptyPlatformList(t, paths, fmt.Sprintf("setup cell %q", value))
}
func validateManifestOnboardingPaths(t *testing.T, platform platformSupportManifestEntry) []string {
t.Helper()
switch platform.GovernanceState {
case "supported", "admitted":
return requireNonEmptyPlatformList(
t,
platform.OnboardingPaths,
fmt.Sprintf("manifest onboarding paths for %s", platform.ID),
)
case "presentation-only":
if len(platform.OnboardingPaths) != 0 {
t.Fatalf(
"presentation-only platform %s must not declare onboarding paths, got %v",
platform.ID,
platform.OnboardingPaths,
)
}
return []string{}
default:
t.Fatalf("unexpected governance state %s for %s", platform.GovernanceState, platform.ID)
return nil
}
}
func indentDiff(diff string) string {
lines := strings.Split(diff, "\n")
for i, line := range lines {
lines[i] = " " + line
}
return strings.Join(lines, "\n")
}