package mapping

import (
	"context"
	"errors"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/pingcap-inc/nexuscrm-contract/pkg/appauthority"
	catalogsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	approvalnotificationsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/approvalnotificationflowv1"
	approvalsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/approvalv1"
	commandsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commandv1"
	commonsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
	permissionsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/effectivepermissionsv1"
	filessemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/filesv1"
	flowsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/flowv1"
	metadataadminsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/metadataadminreadv1"
	recordsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordsv1"
	reportsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/reportv1"
	searchsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/searchv1"
	servingbindingsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/servingbindingv1"
	catalogv1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/catalog/v1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"google.golang.org/protobuf/proto"
)

func TestCatalogConsumerMappingsPreserveScopedImmutableViews(t *testing.T) {
	ctx, cancel := mappingTestContext(t)
	defer cancel()
	minimum := uint64(6)
	get := catalogsemantic.GetRequest{MinCatalogRevision: &minimum}
	wireGet, err := ToWireCatalogGetRequest(ctx, get)
	if err != nil {
		t.Fatal(err)
	}
	roundGet, err := FromWireCatalogGetRequest(ctx, wireGet)
	if err != nil || !reflect.DeepEqual(roundGet, get) {
		t.Fatalf("get round trip = %#v, %v", roundGet, err)
	}
	wireGetV2, err := ToWireCatalogGetV2Request(ctx, get)
	if err != nil {
		t.Fatal(err)
	}
	roundGetV2, err := FromWireCatalogGetV2Request(ctx, wireGetV2)
	if err != nil || !reflect.DeepEqual(roundGetV2, get) {
		t.Fatalf("v2 get round trip = %#v, %v", roundGetV2, err)
	}
	wireGetV3, err := ToWireCatalogGetV3Request(ctx, get)
	if err != nil {
		t.Fatal(err)
	}
	roundGetV3, err := FromWireCatalogGetV3Request(ctx, wireGetV3)
	if err != nil || !reflect.DeepEqual(roundGetV3, get) {
		t.Fatalf("v3 get round trip = %#v, %v", roundGetV3, err)
	}
	wireIntegrationGet, err := ToWireIntegrationBindingGetRequest(ctx, get)
	if err != nil {
		t.Fatal(err)
	}
	roundIntegrationGet, err := FromWireIntegrationBindingGetRequest(ctx, wireIntegrationGet)
	if err != nil || !reflect.DeepEqual(roundIntegrationGet, get) {
		t.Fatalf("integration get round trip = %#v, %v", roundIntegrationGet, err)
	}

	watch := catalogsemantic.WatchRequest{AfterCatalogRevision: &minimum}
	wireAppWatch, err := ToWireAppMetadataWatchRequest(ctx, watch)
	if err != nil {
		t.Fatal(err)
	}
	roundAppWatch, err := FromWireAppMetadataWatchRequest(ctx, wireAppWatch)
	if err != nil || !reflect.DeepEqual(roundAppWatch, watch) {
		t.Fatalf("app watch round trip = %#v, %v", roundAppWatch, err)
	}
	wireAppV2Watch, err := ToWireAppMetadataV2WatchRequest(ctx, watch)
	if err != nil {
		t.Fatal(err)
	}
	roundAppV2Watch, err := FromWireAppMetadataV2WatchRequest(ctx, wireAppV2Watch)
	if err != nil || !reflect.DeepEqual(roundAppV2Watch, watch) {
		t.Fatalf("v2 app watch round trip = %#v, %v", roundAppV2Watch, err)
	}
	wireAppV3Watch, err := ToWireAppMetadataV3WatchRequest(ctx, watch)
	if err != nil {
		t.Fatal(err)
	}
	roundAppV3Watch, err := FromWireAppMetadataV3WatchRequest(ctx, wireAppV3Watch)
	if err != nil || !reflect.DeepEqual(roundAppV3Watch, watch) {
		t.Fatalf("v3 app watch round trip = %#v, %v", roundAppV3Watch, err)
	}
	wireIntegrationWatch, err := ToWireIntegrationBindingWatchRequest(ctx, watch)
	if err != nil {
		t.Fatal(err)
	}
	roundIntegrationWatch, err := FromWireIntegrationBindingWatchRequest(ctx, wireIntegrationWatch)
	if err != nil || !reflect.DeepEqual(roundIntegrationWatch, watch) {
		t.Fatalf("integration watch round trip = %#v, %v", roundIntegrationWatch, err)
	}

	appView := mappingTestAppMetadataView(t, 7)
	wireApp, err := ToWireGetAppMetadataViewResponse(appView)
	if err != nil {
		t.Fatal(err)
	}
	roundApp, err := FromWireGetAppMetadataViewResponse(wireApp)
	if err != nil || !reflect.DeepEqual(roundApp, appView) {
		t.Fatalf("App view round trip = %#v, %v", roundApp, err)
	}
	wireAppWatchResponse, err := ToWireWatchAppMetadataViewResponse(appView)
	if err != nil {
		t.Fatal(err)
	}
	roundAppWatchResponse, err := FromWireWatchAppMetadataViewResponse(wireAppWatchResponse)
	if err != nil || !reflect.DeepEqual(roundAppWatchResponse, appView) {
		t.Fatalf("App watch view round trip = %#v, %v", roundAppWatchResponse, err)
	}
	appViewV2 := mappingTestAppMetadataViewV2(t, 7)
	wireAppV2, err := ToWireGetAppMetadataViewV2Response(appViewV2)
	if err != nil {
		t.Fatal(err)
	}
	roundAppV2, err := FromWireGetAppMetadataViewV2Response(wireAppV2)
	if err != nil || !reflect.DeepEqual(roundAppV2, appViewV2) {
		t.Fatalf("App v2 view round trip = %#v, %v", roundAppV2, err)
	}
	wireAppV2WatchResponse, err :=
		ToWireWatchAppMetadataViewV2Response(appViewV2)
	if err != nil {
		t.Fatal(err)
	}
	roundAppV2WatchResponse, err :=
		FromWireWatchAppMetadataViewV2Response(wireAppV2WatchResponse)
	if err != nil || !reflect.DeepEqual(
		roundAppV2WatchResponse, appViewV2,
	) {
		t.Fatalf(
			"App v2 watch view round trip = %#v, %v",
			roundAppV2WatchResponse, err,
		)
	}
	appViewV3 := mappingTestAppMetadataViewV3(t, 7)
	wireAppV3, err := ToWireGetAppMetadataViewV3Response(appViewV3)
	if err != nil {
		t.Fatal(err)
	}
	roundAppV3, err := FromWireGetAppMetadataViewV3Response(wireAppV3)
	if err != nil || !reflect.DeepEqual(roundAppV3, appViewV3) {
		t.Fatalf("App v3 view round trip = %#v, %v", roundAppV3, err)
	}
	wireAppV3WatchResponse, err := ToWireWatchAppMetadataViewV3Response(appViewV3)
	if err != nil {
		t.Fatal(err)
	}
	roundAppV3WatchResponse, err := FromWireWatchAppMetadataViewV3Response(wireAppV3WatchResponse)
	if err != nil || !reflect.DeepEqual(roundAppV3WatchResponse, appViewV3) {
		t.Fatalf("App v3 watch view round trip = %#v, %v", roundAppV3WatchResponse, err)
	}

	integrationView := mappingTestIntegrationBindingView(7)
	wireIntegration, err := ToWireGetIntegrationBindingViewResponse(integrationView)
	if err != nil {
		t.Fatal(err)
	}
	roundIntegration, err := FromWireGetIntegrationBindingViewResponse(wireIntegration)
	if err != nil || !reflect.DeepEqual(roundIntegration, integrationView) {
		t.Fatalf("Integration view round trip = %#v, %v", roundIntegration, err)
	}
	wireIntegrationWatchResponse, err := ToWireWatchIntegrationBindingViewResponse(integrationView)
	if err != nil {
		t.Fatal(err)
	}
	roundIntegrationWatchResponse, err := FromWireWatchIntegrationBindingViewResponse(wireIntegrationWatchResponse)
	if err != nil || !reflect.DeepEqual(roundIntegrationWatchResponse, integrationView) {
		t.Fatalf("Integration watch view round trip = %#v, %v", roundIntegrationWatchResponse, err)
	}

	wireIntegration.View.Bindings[0].Kind = catalogv1.IntegrationBindingKind_INTEGRATION_BINDING_KIND_UNSPECIFIED
	requirePlatformFailureCategory(t, func() error {
		_, err := FromWireGetIntegrationBindingViewResponse(wireIntegration)
		return err
	}(), commonsemantic.FailureInternal)

	if _, ok := toWireIntegrationBindingKind(catalogsemantic.IntegrationBindingKind("FUTURE_BINDING_KIND")); ok {
		t.Fatal("unmapped future Integration binding kind was accepted")
	}
	for _, kind := range []catalogsemantic.IntegrationBindingKind{
		catalogsemantic.BindingConnectorInboundRoute,
		catalogsemantic.BindingConnectorCalloutProfile,
		catalogsemantic.BindingConnectorFieldMapping,
		catalogsemantic.BindingConnectorPushContract,
	} {
		wireKind, ok := toWireIntegrationBindingKind(kind)
		if !ok {
			t.Fatalf("released Integration binding kind %q was rejected", kind)
		}
		roundKind, ok := fromWireIntegrationBindingKind(wireKind)
		if !ok || roundKind != kind {
			t.Fatalf("Integration binding kind round trip = %q, %v; want %q", roundKind, ok, kind)
		}
	}
}

func TestRuntimePlatformMappingsRoundTripEveryReleasedCapability(t *testing.T) {
	ctx, cancel := mappingTestContext(t)
	defer cancel()
	revision := uint64(7)

	permissionRequest := permissionsemantic.Request{
		TargetPrincipalID: "principal-2", MinCatalogRevision: &revision,
		Selection: &permissionsemantic.Selection{
			Sections: []permissionsemantic.Section{
				permissionsemantic.SectionObjectPermissions,
				permissionsemantic.SectionFieldPermissions,
				permissionsemantic.SectionRecordTypePermissions,
			},
			ResourceTypes: []string{"Account", "Contact"},
		},
	}
	wirePermissionRequest, err := ToWireReadEffectivePermissionsRequest(ctx, permissionRequest)
	if err != nil {
		t.Fatal(err)
	}
	roundPermissionRequest, err := FromWireReadEffectivePermissionsRequest(ctx, wirePermissionRequest)
	if err != nil || !reflect.DeepEqual(roundPermissionRequest, permissionRequest) {
		t.Fatalf("permission request round trip = %#v, %v", roundPermissionRequest, err)
	}
	permissionResponse := permissionsemantic.Response{
		SystemPermissions: []permissionsemantic.PermissionGrant{{
			PermissionID: "ViewSetup", Granted: true,
		}},
		CustomPermissions: []permissionsemantic.PermissionGrant{{
			PermissionID: "CanExport", Granted: false,
		}},
		ObjectPermissions: []permissionsemantic.ObjectPermission{{
			ResourceType: "Account", CanRead: true, CanCreate: true,
		}},
		FieldPermissions: []permissionsemantic.FieldPermission{{
			ResourceType: "Account", FieldName: "Name", CanRead: true, CanCreate: true,
		}},
		ApplicationPermissions: []permissionsemantic.ApplicationPermission{{
			ApplicationID: "Sales", CanView: true, IsDefault: true,
		}},
		NavigationPermissions: []permissionsemantic.NavigationPermission{{
			NavigationKey: "Account", CanView: true,
		}},
		RecordTypePermissions: []permissionsemantic.RecordTypePermission{{
			ResourceType: "Account", RecordTypeID: "rt-master",
			Available: true, IsDefault: true,
		}},
		Receipts: mappingTestReceipts(permissionsemantic.CapabilityName, revision),
	}
	wirePermissionResponse, err := ToWireReadEffectivePermissionsResponse(permissionResponse)
	if err != nil {
		t.Fatal(err)
	}
	roundPermissionResponse, err := FromWireReadEffectivePermissionsResponse(wirePermissionResponse)
	if err != nil || !reflect.DeepEqual(roundPermissionResponse, permissionResponse) {
		t.Fatalf("permission response round trip = %#v, %v", roundPermissionResponse, err)
	}
	if _, err := ToWireReadEffectivePermissionsResponseForRequest(permissionRequest, permissionResponse); err == nil {
		t.Fatal("selected permission mapper accepted unrequested response sections")
	}
	selectedPermissionResponse := permissionResponse
	selectedPermissionResponse.SystemPermissions = []permissionsemantic.PermissionGrant{}
	selectedPermissionResponse.CustomPermissions = []permissionsemantic.PermissionGrant{}
	selectedPermissionResponse.ApplicationPermissions = []permissionsemantic.ApplicationPermission{}
	selectedPermissionResponse.NavigationPermissions = []permissionsemantic.NavigationPermission{}
	wireSelectedPermissionResponse, err := ToWireReadEffectivePermissionsResponseForRequest(
		permissionRequest, selectedPermissionResponse,
	)
	if err != nil {
		t.Fatal(err)
	}
	roundSelectedPermissionResponse, err := FromWireReadEffectivePermissionsResponseForRequest(
		permissionRequest, wireSelectedPermissionResponse,
	)
	if err != nil || !reflect.DeepEqual(roundSelectedPermissionResponse, selectedPermissionResponse) {
		t.Fatalf("selected permission response round trip = %#v, %v", roundSelectedPermissionResponse, err)
	}
	wireSelectedPermissionResponse.SystemPermissions = []*runtimev1.EffectivePermissionGrant{{
		PermissionId: "ViewSetup", Granted: true,
	}}
	if _, err := FromWireReadEffectivePermissionsResponseForRequest(
		permissionRequest, wireSelectedPermissionResponse,
	); err == nil {
		t.Fatal("selected permission wire mapper accepted an unrequested response section")
	}
	wirePermissionResponse.ObjectPermissions = append(
		wirePermissionResponse.ObjectPermissions, (*runtimev1.EffectiveObjectPermission)(nil),
	)
	requirePlatformFailureCategory(t, func() error {
		_, err := FromWireReadEffectivePermissionsResponse(wirePermissionResponse)
		return err
	}(), commonsemantic.FailureInternal)
	wirePermissionResponse, err = ToWireReadEffectivePermissionsResponse(permissionResponse)
	if err != nil {
		t.Fatal(err)
	}
	wirePermissionResponse.RecordTypePermissions = append(
		wirePermissionResponse.RecordTypePermissions, (*runtimev1.EffectiveRecordTypePermission)(nil),
	)
	requirePlatformFailureCategory(t, func() error {
		_, err := FromWireReadEffectivePermissionsResponse(wirePermissionResponse)
		return err
	}(), commonsemantic.FailureInternal)

	searchRequest := searchsemantic.Request{
		ObjectIDs: []string{"Account"}, Term: "acme", SearchFields: []string{"Name"},
		SelectedFields: []string{"Name"}, Limit: 10, MinCatalogRevision: &revision,
	}
	wireSearchRequest, err := ToWireSearchRequest(ctx, searchRequest)
	if err != nil {
		t.Fatal(err)
	}
	roundSearchRequest, err := FromWireSearchRequest(ctx, wireSearchRequest)
	if err != nil || !reflect.DeepEqual(roundSearchRequest, searchRequest) {
		t.Fatalf("search request round trip = %#v, %v", roundSearchRequest, err)
	}
	plannedSearchRequest := searchsemantic.Request{
		Term: "acme", Limit: 20, MinCatalogRevision: &revision,
		ObjectPlans: []searchsemantic.ObjectPlan{
			{ObjectID: "Account", SearchFields: []string{"Name", "Website"}, SelectedFields: []string{"Name"}, Limit: 10},
			{ObjectID: "Contact", SearchFields: []string{"Name", "Email"}, SelectedFields: []string{"Name", "Email"}, Limit: 10},
		},
	}
	wirePlannedSearchRequest, err := ToWireSearchRequest(ctx, plannedSearchRequest)
	if err != nil {
		t.Fatal(err)
	}
	roundPlannedSearchRequest, err := FromWireSearchRequest(ctx, wirePlannedSearchRequest)
	if err != nil || !reflect.DeepEqual(roundPlannedSearchRequest, plannedSearchRequest) {
		t.Fatalf("planned search request round trip = %#v, %v", roundPlannedSearchRequest, err)
	}
	wirePlannedSearchRequest.ObjectPlans = append(
		wirePlannedSearchRequest.ObjectPlans, (*runtimev1.SearchObjectPlan)(nil),
	)
	requirePlatformFailureCategory(t, func() error {
		_, err := FromWireSearchRequest(ctx, wirePlannedSearchRequest)
		return err
	}(), commonsemantic.FailureInternal)
	name := "Acme"
	score := 0.75
	searchResponse := searchsemantic.Response{
		Records: []searchsemantic.Record{{
			ObjectID: "Account", RecordID: "001",
			Fields: []recordsemantic.Field{{Name: "Name", Kind: recordsemantic.ValueKindString, String: &name}},
			Score:  &score,
		}},
		Receipts: mappingTestReceipts(searchsemantic.CapabilityName, revision),
	}
	wireSearchResponse, err := ToWireSearchResponse(searchRequest, searchResponse)
	if err != nil {
		t.Fatal(err)
	}
	roundSearchResponse, err := FromWireSearchResponse(searchRequest, wireSearchResponse)
	if err != nil || !reflect.DeepEqual(roundSearchResponse, searchResponse) {
		t.Fatalf("search response round trip = %#v, %v", roundSearchResponse, err)
	}

	searchScopeRequest := searchsemantic.ScopeOrderRequest{
		ObjectIDs: []string{"Account", "Contact", "Lead"}, MinCatalogRevision: &revision,
	}
	wireSearchScopeRequest, err := ToWireSearchScopeOrderRequest(ctx, searchScopeRequest)
	if err != nil {
		t.Fatal(err)
	}
	roundSearchScopeRequest, err := FromWireSearchScopeOrderRequest(ctx, wireSearchScopeRequest)
	if err != nil || !reflect.DeepEqual(roundSearchScopeRequest, searchScopeRequest) {
		t.Fatalf("search scope request round trip = %#v, %v", roundSearchScopeRequest, err)
	}
	searchScopeResponse := searchsemantic.ScopeOrderResponse{
		ObjectIDs: []string{"Lead", "Account"},
		Receipts:  mappingTestReceipts(searchsemantic.CapabilityName, revision),
	}
	wireSearchScopeResponse, err := ToWireSearchScopeOrderResponse(searchScopeRequest, searchScopeResponse)
	if err != nil {
		t.Fatal(err)
	}
	roundSearchScopeResponse, err := FromWireSearchScopeOrderResponse(searchScopeRequest, wireSearchScopeResponse)
	if err != nil || !reflect.DeepEqual(roundSearchScopeResponse, searchScopeResponse) {
		t.Fatalf("search scope response round trip = %#v, %v", roundSearchScopeResponse, err)
	}
	wireSearchScopeResponse.ObjectIds = append(wireSearchScopeResponse.ObjectIds, "Opportunity")
	requirePlatformFailureCategory(t, func() error {
		_, err := FromWireSearchScopeOrderResponse(searchScopeRequest, wireSearchScopeResponse)
		return err
	}(), commonsemantic.FailureInternal)

	flowRequest := flowsemantic.Request{
		DefinitionRef: "flow.neutral", DefinitionVersion: "version-7", IdempotencyKey: "request-7",
		CanonicalInputs: []byte(`{"record":"001"}`), MinCatalogRevision: &revision,
	}
	wireFlowRequest, err := ToWireExecuteFlowRequest(ctx, flowRequest)
	if err != nil {
		t.Fatal(err)
	}
	roundFlowRequest, err := FromWireExecuteFlowRequest(ctx, wireFlowRequest)
	if err != nil || !reflect.DeepEqual(roundFlowRequest, flowRequest) {
		t.Fatalf("flow request round trip = %#v, %v", roundFlowRequest, err)
	}
	actionRequest := flowsemantic.CatalogActionRequest{
		IdempotencyKey: "action-request-7", CanonicalInputs: []byte(`{"record":"001"}`),
		MinCatalogRevision: &revision,
		CatalogAction: flowsemantic.CatalogActionBinding{
			ComponentRef: "bootstrap-actions", ActionRef: "convert",
		},
	}
	wireActionRequest, err := ToWireExecuteCatalogActionRequest(ctx, actionRequest)
	if err != nil || wireActionRequest.GetDefinitionRef() != "" ||
		wireActionRequest.GetDefinitionVersion() != "" {
		t.Fatalf("Catalog action wire request = %#v, %v", wireActionRequest, err)
	}
	roundActionRequest, err := FromWireExecuteCatalogActionRequest(ctx, wireActionRequest)
	if err != nil || !reflect.DeepEqual(roundActionRequest, actionRequest) {
		t.Fatalf("Catalog action request round trip = %#v, %v", roundActionRequest, err)
	}
	previewRequest := flowsemantic.CatalogActionPreviewRequest{
		Mode:               flowsemantic.CatalogActionPreviewCreateRecords,
		CanonicalInputs:    []byte(`{"record":"001"}`),
		MinCatalogRevision: &revision,
		CatalogAction: flowsemantic.CatalogActionBinding{
			ComponentRef: "bootstrap-actions", ActionRef: "create-target",
		},
	}
	wirePreviewRequest, err := ToWirePreviewCatalogActionRequest(ctx, previewRequest)
	if err != nil {
		t.Fatal(err)
	}
	roundPreviewRequest, err := FromWirePreviewCatalogActionRequest(ctx, wirePreviewRequest)
	if err != nil || !reflect.DeepEqual(roundPreviewRequest, previewRequest) {
		t.Fatalf("Catalog action preview request round trip = %#v, %v", roundPreviewRequest, err)
	}
	outputPreviewRequest := previewRequest
	outputPreviewRequest.Mode = flowsemantic.CatalogActionPreviewOutputs
	outputPreviewRequest.CanonicalInputs = nil
	outputPreviewRequest.RecordID = "001-record"
	wireOutputPreviewRequest, err := ToWirePreviewCatalogActionRequest(ctx, outputPreviewRequest)
	if err != nil {
		t.Fatal(err)
	}
	roundOutputPreviewRequest, err := FromWirePreviewCatalogActionRequest(ctx, wireOutputPreviewRequest)
	if err != nil || !reflect.DeepEqual(roundOutputPreviewRequest, outputPreviewRequest) {
		t.Fatalf("Catalog action output preview request round trip = %#v, %v", roundOutputPreviewRequest, err)
	}
	previewResponse := flowsemantic.CatalogActionPreviewResponse{
		CanonicalResult: []byte(`{"creates":[]}`),
		Receipts:        mappingTestReceipts(flowsemantic.CapabilityName, revision),
	}
	wirePreviewResponse, err := ToWirePreviewCatalogActionResponse(previewRequest, previewResponse)
	if err != nil {
		t.Fatal(err)
	}
	roundPreviewResponse, err := FromWirePreviewCatalogActionResponse(previewRequest, wirePreviewResponse)
	if err != nil || !reflect.DeepEqual(roundPreviewResponse, previewResponse) {
		t.Fatalf("Catalog action preview response round trip = %#v, %v", roundPreviewResponse, err)
	}
	mixedActionRequest := proto.Clone(wireActionRequest).(*runtimev1.ExecuteFlowRequest)
	mixedActionRequest.DefinitionRef = flowRequest.DefinitionRef
	mixedActionRequest.DefinitionVersion = flowRequest.DefinitionVersion
	requirePlatformFailureCategory(t, func() error {
		_, err := FromWireExecuteFlowRequest(ctx, mixedActionRequest)
		return err
	}(), commonsemantic.FailureInvalidRequest)
	requirePlatformFailureCategory(t, func() error {
		_, err := FromWireExecuteCatalogActionRequest(ctx, mixedActionRequest)
		return err
	}(), commonsemantic.FailureInvalidRequest)
	downgradedActionRequest := proto.Clone(wireActionRequest).(*runtimev1.ExecuteFlowRequest)
	downgradedActionRequest.CatalogAction = nil
	requirePlatformFailureCategory(t, func() error {
		_, err := FromWireExecuteFlowRequest(ctx, downgradedActionRequest)
		return err
	}(), commonsemantic.FailureInvalidRequest)
	flowResponse := flowsemantic.Response{
		ExecutionID: "execution-7", CanonicalOutputs: []byte(`{"saved":true}`),
		Receipts: mappingTestReceipts(flowsemantic.CapabilityName, revision),
	}
	wireFlowResponse, err := ToWireExecuteFlowResponse(flowResponse)
	if err != nil {
		t.Fatal(err)
	}
	roundFlowResponse, err := FromWireExecuteFlowResponse(wireFlowResponse)
	if err != nil || !reflect.DeepEqual(roundFlowResponse, flowResponse) {
		t.Fatalf("flow response round trip = %#v, %v", roundFlowResponse, err)
	}

	reportRequest := reportsemantic.Request{
		ReportID: "report-7", CanonicalFilters: []byte(`{"owner":"me"}`),
		Limit: 10, Offset: 2, MinCatalogRevision: &revision,
	}
	wireReportRequest, err := ToWireReadReportRequest(ctx, reportRequest)
	if err != nil {
		t.Fatal(err)
	}
	roundReportRequest, err := FromWireReadReportRequest(ctx, wireReportRequest)
	if err != nil || !reflect.DeepEqual(roundReportRequest, reportRequest) {
		t.Fatalf("report request round trip = %#v, %v", roundReportRequest, err)
	}
	reportResponse := reportsemantic.Response{
		CanonicalResult: []byte(`{"rows":[]}`),
		Receipts:        mappingTestReceipts(reportsemantic.CapabilityName, revision),
	}
	wireReportResponse, err := ToWireReadReportResponse(reportResponse)
	if err != nil {
		t.Fatal(err)
	}
	roundReportResponse, err := FromWireReadReportResponse(wireReportResponse)
	if err != nil || !reflect.DeepEqual(roundReportResponse, reportResponse) {
		t.Fatalf("report response round trip = %#v, %v", roundReportResponse, err)
	}

	payload := []byte(`{"event":"created"}`)
	commandRequest := commandsemantic.Request{
		CommandID: "command-7", IdempotencyKey: "request-7", CommandKind: "connector.event",
		PartitionKey: "account-001", PayloadFormatVersion: 1, Payload: payload,
		PayloadDigest: commandsemantic.ComputePayloadDigest(payload), MinCatalogRevision: &revision,
	}
	wireCommandRequest, err := ToWireAcceptCommandRequest(ctx, commandRequest)
	if err != nil {
		t.Fatal(err)
	}
	roundCommandRequest, err := FromWireAcceptCommandRequest(ctx, wireCommandRequest)
	if err != nil || !reflect.DeepEqual(roundCommandRequest, commandRequest) {
		t.Fatalf("command request round trip = %#v, %v", roundCommandRequest, err)
	}
	commandResponse := commandsemantic.Response{
		CommandID: commandRequest.CommandID, Duplicate: true,
		Receipts: mappingTestReceipts(commandsemantic.CapabilityName, revision),
	}
	wireCommandResponse, err := ToWireAcceptCommandResponse(commandRequest, commandResponse)
	if err != nil {
		t.Fatal(err)
	}
	roundCommandResponse, err := FromWireAcceptCommandResponse(commandRequest, wireCommandResponse)
	if err != nil || !reflect.DeepEqual(roundCommandResponse, commandResponse) {
		t.Fatalf("command response round trip = %#v, %v", roundCommandResponse, err)
	}
}

func TestAppMetadataV2WireVersionIsFirstVersionOfNewIdentity(t *testing.T) {
	view := mappingTestAppMetadataViewV2(t, 7)
	wireView, err := ToWireGetAppMetadataViewV2Response(view)
	if err != nil {
		t.Fatal(err)
	}
	version := wireView.GetView().GetReceipts().GetCapabilityVersion()
	if version.GetName() != catalogsemantic.AppMetadataV2CapabilityName ||
		version.GetMajor() != 1 || version.GetMinor() != 0 {
		t.Fatalf("App metadata v2 wire capability = %#v, want new identity at wire 1.0", version)
	}

	// The ".v2" suffix belongs to the immutable semantic identity; it does not
	// make 2.0 a compatible numeric wire version for that new identity.
	version.Major = 2
	requirePlatformFailureCategory(t, func() error {
		_, err := FromWireGetAppMetadataViewV2Response(wireView)
		return err
	}(), commonsemantic.FailureIncompatible)
}

func TestAppMetadataV3WireVersionIsFirstVersionOfNewIdentity(t *testing.T) {
	view := mappingTestAppMetadataViewV3(t, 7)
	wireView, err := ToWireGetAppMetadataViewV3Response(view)
	if err != nil {
		t.Fatal(err)
	}
	version := wireView.GetView().GetReceipts().GetCapabilityVersion()
	if version.GetName() != catalogsemantic.AppMetadataV3CapabilityName ||
		version.GetMajor() != 1 || version.GetMinor() != 0 {
		t.Fatalf("App metadata v3 wire capability = %#v, want new identity at wire 1.0", version)
	}

	version.Name = catalogsemantic.AppMetadataV2CapabilityName
	requirePlatformFailureCategory(t, func() error {
		_, err := FromWireGetAppMetadataViewV3Response(wireView)
		return err
	}(), commonsemantic.FailureIncompatible)
}

func TestCatalogServingBindingWirePreservesExactV1Identity(t *testing.T) {
	binding := mappingTestCatalogServingBinding(7)
	wireBinding, err := ToWireCatalogServingBinding(binding)
	if err != nil {
		t.Fatal(err)
	}
	round, err := FromWireCatalogServingBinding(wireBinding)
	if err != nil || !reflect.DeepEqual(round, binding) {
		t.Fatalf("Catalog serving binding round trip = %#v, %v", round, err)
	}
	version := wireBinding.GetReceipts().GetCapabilityVersion()
	if version.GetName() != servingbindingsemantic.CapabilityName ||
		version.GetMajor() != 1 || version.GetMinor() != 0 {
		t.Fatalf("Catalog serving binding wire capability = %#v, want new identity at wire 1.0", version)
	}

	version.Name = "runtime.catalog-serving-binding.v0"
	requirePlatformFailureCategory(t, func() error {
		_, err := FromWireCatalogServingBinding(wireBinding)
		return err
	}(), commonsemantic.FailureIncompatible)
}

func TestFileMappingsPreserveBoundedStreamGrammar(t *testing.T) {
	ctx, cancel := mappingTestContext(t)
	defer cancel()
	revision := uint64(7)
	header := filessemantic.CreateHeader{
		IdempotencyKey: "request-7", ParentObjectID: "Account", ParentRecordID: "001",
		FileName: "evidence.txt", ContentType: "text/plain", ContentLength: 3,
		AttachmentKind: "record.attachment", MinCatalogRevision: &revision,
	}
	for _, chunk := range []filessemantic.CreateChunk{{Header: &header}, {Data: []byte("abc")}} {
		wireChunk, err := ToWireCreateFileChunk(chunk)
		if err != nil {
			t.Fatal(err)
		}
		roundChunk, err := FromWireCreateFileChunk(wireChunk)
		if err != nil || !reflect.DeepEqual(roundChunk, chunk) {
			t.Fatalf("create chunk round trip = %#v, %v", roundChunk, err)
		}
	}
	createResponse := filessemantic.CreateResponse{
		FileID: "file-7", FileName: header.FileName, Size: 3, ContentType: header.ContentType,
		Receipts: mappingTestReceipts(filessemantic.CapabilityName, revision),
	}
	wireCreate, err := ToWireCreateFileResponse(createResponse)
	if err != nil {
		t.Fatal(err)
	}
	roundCreate, err := FromWireCreateFileResponse(wireCreate)
	if err != nil || !reflect.DeepEqual(roundCreate, createResponse) {
		t.Fatalf("create response round trip = %#v, %v", roundCreate, err)
	}

	openRequest := filessemantic.OpenRequest{FileID: createResponse.FileID, MinCatalogRevision: &revision}
	wireOpenRequest, err := ToWireOpenFileRequest(ctx, openRequest)
	if err != nil {
		t.Fatal(err)
	}
	roundOpenRequest, err := FromWireOpenFileRequest(ctx, wireOpenRequest)
	if err != nil || !reflect.DeepEqual(roundOpenRequest, openRequest) {
		t.Fatalf("open request round trip = %#v, %v", roundOpenRequest, err)
	}
	receipts := mappingTestReceipts(filessemantic.CapabilityName, revision)
	openChunk := filessemantic.OpenChunk{
		FileName: header.FileName, ContentType: header.ContentType, TotalSize: 3,
		Data: []byte("abc"), Receipts: &receipts,
	}
	wireOpenChunk, err := ToWireOpenFileChunk(openChunk)
	if err != nil {
		t.Fatal(err)
	}
	roundOpenChunk, err := FromWireOpenFileChunk(wireOpenChunk)
	if err != nil || !reflect.DeepEqual(roundOpenChunk, openChunk) {
		t.Fatalf("open chunk round trip = %#v, %v", roundOpenChunk, err)
	}

	deleteRequest := filessemantic.DeleteRequest{FileID: createResponse.FileID, MinCatalogRevision: &revision}
	wireDeleteRequest, err := ToWireDeleteFileRequest(ctx, deleteRequest)
	if err != nil {
		t.Fatal(err)
	}
	roundDeleteRequest, err := FromWireDeleteFileRequest(ctx, wireDeleteRequest)
	if err != nil || !reflect.DeepEqual(roundDeleteRequest, deleteRequest) {
		t.Fatalf("delete request round trip = %#v, %v", roundDeleteRequest, err)
	}
	deleteResponse := filessemantic.DeleteResponse{Receipts: receipts}
	wireDeleteResponse, err := ToWireDeleteFileResponse(deleteResponse)
	if err != nil {
		t.Fatal(err)
	}
	roundDeleteResponse, err := FromWireDeleteFileResponse(wireDeleteResponse)
	if err != nil || !reflect.DeepEqual(roundDeleteResponse, deleteResponse) {
		t.Fatalf("delete response round trip = %#v, %v", roundDeleteResponse, err)
	}
}

func TestPlatformStatusMappingIsCapabilityBoundAndFailsClosed(t *testing.T) {
	receipts := mappingTestReceipts(searchsemantic.CapabilityName, 7)
	source := &commonsemantic.Failure{
		Category: commonsemantic.FailureStaleState, StaleAxis: commonsemantic.StaleAxisCatalog,
		RequiredRevision: 8, ServedRevision: 7, Receipts: &receipts,
	}
	wireError := ToPlatformStatusError(source, searchsemantic.CapabilityName)
	decoded := FromPlatformStatusError(wireError, searchsemantic.CapabilityName)
	var failure *commonsemantic.Failure
	if !errors.As(decoded, &failure) || failure.Category != source.Category ||
		failure.StaleAxis != source.StaleAxis || !reflect.DeepEqual(failure.Receipts, source.Receipts) {
		t.Fatalf("status round trip = %#v", decoded)
	}
	requirePlatformFailureCategory(
		t, FromPlatformStatusError(wireError, flowsemantic.CapabilityName), commonsemantic.FailureInternal,
	)
	requirePlatformFailureCategory(
		t,
		FromPlatformStatusError(ToPlatformStatusError(&commonsemantic.Failure{
			Category:  commonsemantic.FailureResourceExhausted,
			LimitKind: commonsemantic.LimitKind("SECRET_INTERNAL_LIMIT"),
		}, searchsemantic.CapabilityName), searchsemantic.CapabilityName),
		commonsemantic.FailureInternal,
	)
	requirePlatformFailureCategory(
		t, FromPlatformStatusError(wireError, "runtime.unknown.v1"), commonsemantic.FailureInternal,
	)

	validLimit := &commonsemantic.Failure{
		Category:  commonsemantic.FailureResourceExhausted,
		LimitKind: commonsemantic.LimitSemanticRequest, Maximum: searchsemantic.MaxSemanticRequestBytes,
	}
	decodedLimit := FromPlatformStatusError(
		ToPlatformStatusError(validLimit, searchsemantic.CapabilityName),
		searchsemantic.CapabilityName,
	)
	if !errors.As(decodedLimit, &failure) || failure.Category != validLimit.Category ||
		failure.LimitKind != validLimit.LimitKind || failure.Maximum != validLimit.Maximum {
		t.Fatalf("valid Search limit round trip = %#v", decodedLimit)
	}

	v3Limit := &commonsemantic.Failure{
		Category:  commonsemantic.FailureResourceExhausted,
		LimitKind: commonsemantic.LimitSemanticResponse,
		Maximum:   catalogsemantic.MaxAppMetadataV3SemanticResponseBytes,
	}
	decodedV3Limit := FromPlatformStatusError(
		ToPlatformStatusError(v3Limit, catalogsemantic.AppMetadataV3CapabilityName),
		catalogsemantic.AppMetadataV3CapabilityName,
	)
	if !errors.As(decodedV3Limit, &failure) || failure.Category != v3Limit.Category ||
		failure.LimitKind != v3Limit.LimitKind || failure.Maximum != v3Limit.Maximum {
		t.Fatalf("valid AppMetadataV3 limit round trip = %#v", decodedV3Limit)
	}
	v3InvalidResponse := &commonsemantic.Failure{
		Category:         commonsemantic.FailureInvalidRequest,
		InvalidComponent: commonsemantic.InvalidComponentResponse,
	}
	decodedV3InvalidResponse := FromPlatformStatusError(
		ToPlatformStatusError(v3InvalidResponse, catalogsemantic.AppMetadataV3CapabilityName),
		catalogsemantic.AppMetadataV3CapabilityName,
	)
	if !errors.As(decodedV3InvalidResponse, &failure) ||
		failure.Category != v3InvalidResponse.Category ||
		failure.InvalidComponent != v3InvalidResponse.InvalidComponent {
		t.Fatalf("valid AppMetadataV3 invalid response round trip = %#v", decodedV3InvalidResponse)
	}

	validPermissionLimit := &commonsemantic.Failure{
		Category:  commonsemantic.FailureResourceExhausted,
		LimitKind: commonsemantic.LimitFieldCount, Maximum: permissionsemantic.MaxFieldPermissions,
	}
	decodedPermissionLimit := FromPlatformStatusError(
		ToPlatformStatusError(validPermissionLimit, permissionsemantic.CapabilityName),
		permissionsemantic.CapabilityName,
	)
	if !errors.As(decodedPermissionLimit, &failure) ||
		failure.Category != validPermissionLimit.Category ||
		failure.LimitKind != validPermissionLimit.LimitKind ||
		failure.Maximum != validPermissionLimit.Maximum {
		t.Fatalf("valid effective-permission limit round trip = %#v", decodedPermissionLimit)
	}
	recordTypeLimit := &commonsemantic.Failure{
		Category: commonsemantic.FailureResourceExhausted, LimitKind: commonsemantic.LimitOperationCount,
		Maximum: permissionsemantic.MaxRecordTypePermissions,
	}
	decodedRecordTypeLimit := FromPlatformStatusError(
		ToPlatformStatusError(recordTypeLimit, permissionsemantic.CapabilityName),
		permissionsemantic.CapabilityName,
	)
	if !errors.As(decodedRecordTypeLimit, &failure) ||
		failure.Category != recordTypeLimit.Category || failure.LimitKind != recordTypeLimit.LimitKind ||
		failure.Maximum != recordTypeLimit.Maximum {
		t.Fatalf("valid record-type permission limit round trip = %#v", decodedRecordTypeLimit)
	}

	for _, component := range []commonsemantic.InvalidComponent{
		commonsemantic.InvalidComponentCommandData,
		commonsemantic.InvalidComponentOperation,
	} {
		approvalFailure := &commonsemantic.Failure{
			Category:         commonsemantic.FailureInvalidRequest,
			InvalidComponent: component,
		}
		decodedApprovalFailure := FromPlatformStatusError(
			ToPlatformStatusError(approvalFailure, approvalsemantic.ExecuteCapabilityName),
			approvalsemantic.ExecuteCapabilityName,
		)
		if !errors.As(decodedApprovalFailure, &failure) ||
			failure.Category != approvalFailure.Category ||
			failure.InvalidComponent != approvalFailure.InvalidComponent {
			t.Fatalf("valid Approval %s failure round trip = %#v", component, decodedApprovalFailure)
		}
	}
	approvalOperationConflict := &commonsemantic.Failure{
		Category:         commonsemantic.FailureInvalidRequest,
		InvalidComponent: commonsemantic.InvalidComponentOperation,
	}
	requirePlatformFailureCategory(
		t,
		FromPlatformStatusError(
			ToPlatformStatusError(approvalOperationConflict, flowsemantic.CapabilityName),
			flowsemantic.CapabilityName,
		),
		commonsemantic.FailureInternal,
	)

	for _, test := range []struct {
		name       string
		capability string
		failure    *commonsemantic.Failure
	}{
		{
			name: "wrong fixed maximum", capability: searchsemantic.CapabilityName,
			failure: &commonsemantic.Failure{
				Category:  commonsemantic.FailureResourceExhausted,
				LimitKind: commonsemantic.LimitSemanticRequest,
			},
		},
		{
			name: "foreign limit", capability: commandsemantic.CapabilityName,
			failure: &commonsemantic.Failure{
				Category:  commonsemantic.FailureResourceExhausted,
				LimitKind: commonsemantic.LimitWireFrame, Maximum: filessemantic.MaxChunkBytes,
			},
		},
		{
			name: "foreign invalid component", capability: searchsemantic.CapabilityName,
			failure: &commonsemantic.Failure{
				Category:         commonsemantic.FailureInvalidRequest,
				InvalidComponent: commonsemantic.InvalidComponentOperation,
			},
		},
		{
			name: "wrong permission limit maximum", capability: permissionsemantic.CapabilityName,
			failure: &commonsemantic.Failure{
				Category:  commonsemantic.FailureResourceExhausted,
				LimitKind: commonsemantic.LimitFieldCount,
				Maximum:   permissionsemantic.MaxFieldPermissions - 1,
			},
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			requirePlatformFailureCategory(
				t,
				FromPlatformStatusError(
					ToPlatformStatusError(test.failure, test.capability),
					test.capability,
				),
				commonsemantic.FailureInternal,
			)
		})
	}
}

func TestPlatformSemanticShapeFenceRejectsUnmappedFacts(t *testing.T) {
	if exactPlatformSemanticShapes([]platformSemanticShape{{
		value: struct {
			Known string
			Added string
		}{},
		fields: []string{"Known"},
	}}) {
		t.Fatal("semantic shape with an unmapped additive fact was accepted")
	}
	if exactPlatformSemanticShapes([]platformSemanticShape{{
		value:  struct{ Known string }{},
		fields: []string{"Renamed"},
	}}) {
		t.Fatal("semantic shape with a renamed fact was accepted")
	}
}

func TestPlatformSemanticShapeFenceIsCapabilityIsolated(t *testing.T) {
	checks := map[string]*platformSemanticShapeCheck{
		searchsemantic.CapabilityName: {
			shapes: []platformSemanticShape{{
				value: struct {
					Known string
					Added string
				}{},
				fields: []string{"Known"},
			}},
		},
		flowsemantic.CapabilityName: {
			shapes: []platformSemanticShape{{
				value:  struct{ Known string }{},
				fields: []string{"Known"},
			}},
		},
	}
	if err := requirePlatformSemanticShapeFrom(checks, searchsemantic.CapabilityName); err == nil {
		t.Fatal("unmapped search fact was accepted")
	}
	if err := requirePlatformSemanticShapeFrom(checks, flowsemantic.CapabilityName); err != nil {
		t.Fatalf("unrelated flow capability was rejected: %v", err)
	}
}

func TestPlatformSemanticShapeFenceIncludesNestedProjectedFacts(t *testing.T) {
	for capability, check := range platformSemanticShapeChecks {
		if check == nil {
			t.Fatalf("%s has no semantic shape check", capability)
		}
		types := make(map[reflect.Type]bool, len(check.shapes))
		for _, shape := range check.shapes {
			types[reflect.TypeOf(shape.value)] = true
		}
		for _, required := range []any{commonsemantic.Failure{}, commonsemantic.Receipts{}} {
			if !types[reflect.TypeOf(required)] {
				t.Fatalf("%s does not fence nested %T", capability, required)
			}
		}
	}
	searchShapes := platformSemanticShapeChecks[searchsemantic.CapabilityName]
	types := make(map[reflect.Type]bool, len(searchShapes.shapes))
	for _, shape := range searchShapes.shapes {
		types[reflect.TypeOf(shape.value)] = true
	}
	for _, required := range []any{recordsemantic.Decimal{}, recordsemantic.Field{}} {
		if !types[reflect.TypeOf(required)] {
			t.Fatalf("Search does not fence nested %T", required)
		}
	}
	metadataAdminShapes := platformSemanticShapeChecks[metadataadminsemantic.CapabilityName]
	types = make(map[reflect.Type]bool, len(metadataAdminShapes.shapes))
	for _, shape := range metadataAdminShapes.shapes {
		types[reflect.TypeOf(shape.value)] = true
	}
	for _, required := range []any{
		metadataadminsemantic.ValidationRuleAdminIndexRequest{},
		metadataadminsemantic.ValidationRuleRecordSetFilter{},
		metadataadminsemantic.ValidationRuleRecordSetBinding{},
		metadataadminsemantic.ValidationRuleMetadata{},
		metadataadminsemantic.ValidationRuleAdminIndexResponse{},
	} {
		if !types[reflect.TypeOf(required)] {
			t.Fatalf("Metadata admin read does not fence nested %T", required)
		}
	}
	approvalNotificationShapes := platformSemanticShapeChecks[approvalnotificationsemantic.CapabilityName]
	types = make(map[reflect.Type]bool, len(approvalNotificationShapes.shapes))
	for _, shape := range approvalNotificationShapes.shapes {
		types[reflect.TypeOf(shape.value)] = true
	}
	for _, required := range []any{
		approvalnotificationsemantic.Request{},
		approvalnotificationsemantic.FlowDescriptor{},
		approvalnotificationsemantic.Response{},
	} {
		if !types[reflect.TypeOf(required)] {
			t.Fatalf("Approval notification Flow read does not fence nested %T", required)
		}
	}
}

func mappingTestAppMetadataView(t *testing.T, revision uint64) catalogsemantic.AppMetadataView {
	t.Helper()
	behavior := []byte("opaque-behavior")
	component := catalogsemantic.AppMetadataComponent{
		ComponentKind: "metadata.app.neutral.v1", ComponentFormatVersion: 1,
		NamespaceID: "io.nexuscrm.neutral", BusinessKey: "component",
		DefinitionVersion: "1", CanonicalBehavior: behavior,
	}
	component.ComponentID = appauthority.ComponentID(component.NamespaceID, component.ComponentKind, component.BusinessKey)
	var err error
	component.ContentDigest, err = appauthority.ComponentDigest(component.ComponentKind, component.ComponentFormatVersion, behavior)
	if err != nil {
		t.Fatal(err)
	}
	view := catalogsemantic.AppMetadataView{
		OrgID: "org-1", AppInstallationID: "01900000-0000-7000-8000-000000000001",
		AppID: "io.nexuscrm.neutral", AppReleaseDigest: "sha256:" + strings.Repeat("d", 64),
		Lineage: catalogsemantic.ViewLineage{
			CatalogRevision: revision, SchemaGeneration: revision, EngineGeneration: revision,
			SourceSnapshotDigest: "sha256:" + strings.Repeat("b", 64),
		},
		Components: []catalogsemantic.AppMetadataComponent{component},
		Receipts:   mappingTestReceipts(catalogsemantic.AppMetadataCapabilityName, revision),
	}
	view.Lineage.ViewDigest = catalogsemantic.ComputeAppMetadataViewDigest(view)
	return view
}

func mappingTestAppMetadataViewV2(
	t *testing.T,
	revision uint64,
) catalogsemantic.AppMetadataViewV2 {
	t.Helper()
	v1 := mappingTestAppMetadataView(t, revision)
	effective := catalogsemantic.EffectiveMetadataComponent{
		ComponentKind:          catalogsemantic.EffectiveObjectSearchComponentKind,
		ComponentFormatVersion: 1,
		BusinessKey:            catalogsemantic.EffectiveObjectSearchComponentBusinessKey,
		CanonicalBehavior:      []byte(`{"objects":[]}`),
	}
	effective.ContentDigest =
		catalogsemantic.ComputeEffectiveMetadataComponentDigest(
			effective.CanonicalBehavior,
		)
	view := catalogsemantic.AppMetadataViewV2{
		OrgID: v1.OrgID, AppInstallationID: v1.AppInstallationID,
		AppID: v1.AppID, AppReleaseDigest: v1.AppReleaseDigest,
		Lineage:       v1.Lineage,
		AppComponents: v1.Components,
		EffectiveComponents: []catalogsemantic.EffectiveMetadataComponent{
			effective,
		},
		Receipts: mappingTestReceipts(
			catalogsemantic.AppMetadataV2CapabilityName, revision,
		),
	}
	view.Lineage.ViewDigest =
		catalogsemantic.ComputeAppMetadataViewV2Digest(view)
	return view
}

func mappingTestAppMetadataViewV3(
	t *testing.T,
	revision uint64,
) catalogsemantic.AppMetadataViewV3 {
	t.Helper()
	v2 := mappingTestAppMetadataViewV2(t, revision)
	view := catalogsemantic.AppMetadataViewV3{
		OrgID: v2.OrgID, AppInstallationID: v2.AppInstallationID,
		AppID: v2.AppID, AppReleaseDigest: v2.AppReleaseDigest,
		Lineage: v2.Lineage, AppComponents: v2.AppComponents,
		EffectiveComponents: v2.EffectiveComponents,
		Receipts: mappingTestReceipts(
			catalogsemantic.AppMetadataV3CapabilityName, revision,
		),
	}
	view.Lineage.ViewDigest = catalogsemantic.ComputeAppMetadataViewV3Digest(view)
	return view
}

func mappingTestIntegrationBindingView(revision uint64) catalogsemantic.IntegrationBindingView {
	view := catalogsemantic.IntegrationBindingView{
		OrgID: "org-1", AppInstallationID: "01900000-0000-7000-8000-000000000001",
		AuthorizedWorkloadID: "integration",
		Lineage: catalogsemantic.ViewLineage{
			CatalogRevision: revision, SchemaGeneration: revision, EngineGeneration: revision,
			SourceSnapshotDigest: "sha256:" + strings.Repeat("b", 64),
		},
		Bindings: []catalogsemantic.IntegrationBinding{{
			Kind: catalogsemantic.BindingConnectorInboundRoute, OwnerPack: "connector-neutral",
			PackInstallationID: "pack-install-1",
			PackPayloadDigest:  "nexus-pack-sha256-v1:" + strings.Repeat("a", 64),
			BusinessKey:        "event.created", DefinitionVersion: 3, CredentialRefs: []string{"credential-a"},
			RuntimeCommand: &catalogsemantic.RuntimeCommandBinding{
				CommandKind: "runtime.flow.execute", PayloadFormatVersion: 1,
				DefinitionRef: "flow-1", DefinitionVersion: "flow-version-1",
			},
		}},
		Receipts: mappingTestReceipts(catalogsemantic.IntegrationBindingCapabilityName, revision),
	}
	view.Lineage.ViewDigest = catalogsemantic.ComputeIntegrationBindingViewDigest(view)
	return view
}

func mappingTestReceipts(capability string, revision uint64) commonsemantic.Receipts {
	return commonsemantic.Receipts{
		ProtocolVersion: "1.0", CapabilityVersion: capability,
		CatalogRevision: revision, SchemaGeneration: revision, EngineGeneration: revision, SecurityEpoch: 3,
	}
}

func mappingTestCatalogServingBinding(revision uint64) servingbindingsemantic.Binding {
	return servingbindingsemantic.Binding{
		OrgID: "org-1", AppInstallationID: "01900000-0000-7000-8000-000000000001",
		AppReleaseDigest: "sha256:" + strings.Repeat("a", 64), WorkloadID: "workload-1",
		CanonicalResource:         "https://catalog.example/mcp",
		ToolContractDigest:        "sha256:" + strings.Repeat("b", 64),
		ScopeRouteReceiptDigest:   "sha256:" + strings.Repeat("c", 64),
		WorkloadCapabilityCeiling: []string{"runtime.records.query.v1", "runtime.records.read-selected-root.v1"},
		Receipts:                  mappingTestReceipts(servingbindingsemantic.CapabilityName, revision),
	}
}

func mappingTestContext(t *testing.T) (context.Context, context.CancelFunc) {
	t.Helper()
	return context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
}

func requirePlatformFailureCategory(t *testing.T, err error, category commonsemantic.FailureCategory) {
	t.Helper()
	var failure *commonsemantic.Failure
	if !errors.As(err, &failure) || failure.Category != category {
		t.Fatalf("failure = %#v, want %s", err, category)
	}
}
