package records

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

	"github.com/nexuscrm/metadata/catalogsnapshot"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordattachmentsv1"
)

func TestRuntimeAttachmentSourcesProvesCurrentParentFieldBeforeLookup(t *testing.T) {
	view := runtimeAttachmentSourceView(t)
	engine := &runtimeAttachmentSourceEngineStub{
		readable: map[string]bool{"DocumentLink": true},
		rows: map[string]models.SObject{
			"WorkItem/parent-1": {constants.FieldID: "parent-1", "DocumentLink": "before/ABCDEF123456/after"},
		},
	}
	resolver := &runtimeAttachmentSourceResolverStub{results: []semantic.SourceResult{{Index: 0, Outcome: semantic.SourceResolved, FileID: "file-1", FileName: "spec.pdf"}}}
	service := runtimeAttachmentSourceService(t, view, engine, resolver)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	result, err := service.ResolveAttachmentSources(ctx, semantic.ResolveRequest{
		ParentObjectID: "WorkItem", ParentRecordID: "parent-1", ResolverID: "document_publication",
		Sources: []semantic.Source{{SourceFieldID: "DocumentLink", SourceValue: "ABCDEF123456", SourceValueOffset: 7}},
	}, &models.UserSession{ID: "user-1"})
	if err != nil {
		t.Fatal(err)
	}
	want := []semantic.SourceResult{{Index: 0, Outcome: semantic.SourceResolved, FileID: "file-1", FileName: "spec.pdf"}}
	if !reflect.DeepEqual(result, want) || resolver.calls != 1 ||
		!reflect.DeepEqual(resolver.sourceValues, []string{"ABCDEF123456"}) || resolver.parentObjectID != "WorkItem" || resolver.parentRecordID != "parent-1" {
		t.Fatalf("results=%#v resolver=%#v", result, resolver)
	}
}

func TestRuntimeAttachmentSourcesRejectsOverLimitRequestBeforeAllocationOrLookup(t *testing.T) {
	engine := &runtimeAttachmentSourceEngineStub{}
	resolver := &runtimeAttachmentSourceResolverStub{}
	service := runtimeAttachmentSourceService(t, runtimeAttachmentSourceView(t), engine, resolver)
	sources := make([]semantic.Source, semantic.MaxSources+1)
	result, err := service.ResolveAttachmentSources(context.Background(), semantic.ResolveRequest{
		ParentObjectID: "WorkItem", ParentRecordID: "parent-1", ResolverID: "document_publication", Sources: sources,
	}, &models.UserSession{ID: "user-1"})
	var failure *semantic.Failure
	if result != nil || !errors.As(err, &failure) || failure.Category != semantic.FailureResourceExhausted ||
		failure.LimitKind != semantic.LimitOperationCount || failure.Maximum != semantic.MaxSources ||
		engine.retrieveCalls != 0 || resolver.calls != 0 {
		t.Fatalf("result=%#v err=%v failure=%#v reads=%d resolver=%d", result, err, failure, engine.retrieveCalls, resolver.calls)
	}
}

func TestRuntimeAttachmentSourcesProofOffsetsAreRawUTF8Bytes(t *testing.T) {
	view := runtimeAttachmentSourceView(t)
	const source = "ABCDEF123456"
	raw := "é/" + source + "/after"
	engine := &runtimeAttachmentSourceEngineStub{
		readable: map[string]bool{"DocumentLink": true},
		rows:     map[string]models.SObject{"WorkItem/parent-1": {constants.FieldID: "parent-1", "DocumentLink": raw}},
	}
	resolver := &runtimeAttachmentSourceResolverStub{results: []semantic.SourceResult{{Index: 0, Outcome: semantic.SourceNotFound}}}
	service := runtimeAttachmentSourceService(t, view, engine, resolver)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	result, err := service.ResolveAttachmentSources(ctx, semantic.ResolveRequest{
		ParentObjectID: "WorkItem", ParentRecordID: "parent-1", ResolverID: "document_publication",
		Sources: []semantic.Source{{SourceFieldID: "DocumentLink", SourceValue: source, SourceValueOffset: uint32(len("é/"))}},
	}, &models.UserSession{ID: "user-1"})
	if err != nil || !reflect.DeepEqual(result, []semantic.SourceResult{{Index: 0, Outcome: semantic.SourceNotFound}}) ||
		!reflect.DeepEqual(resolver.sourceValues, []string{source}) {
		t.Fatalf("byte-offset proof result=%#v err=%v sourceValues=%#v", result, err, resolver.sourceValues)
	}

	resolver.results = nil
	result, err = service.ResolveAttachmentSources(ctx, semantic.ResolveRequest{
		ParentObjectID: "WorkItem", ParentRecordID: "parent-1", ResolverID: "document_publication",
		// This is the rune offset, not the byte offset. It must never prove the
		// same source value in the raw field.
		Sources: []semantic.Source{{SourceFieldID: "DocumentLink", SourceValue: source, SourceValueOffset: 2}},
	}, &models.UserSession{ID: "user-1"})
	if err != nil || !reflect.DeepEqual(result, []semantic.SourceResult{{Index: 0, Outcome: semantic.SourceUnavailable}}) || resolver.calls != 1 {
		t.Fatalf("rune-offset proof result=%#v err=%v resolverCalls=%d", result, err, resolver.calls)
	}
}

func TestRuntimeAttachmentSourcesMasksBadProofsWithoutSourceLookup(t *testing.T) {
	view := runtimeAttachmentSourceView(t)
	request := semantic.ResolveRequest{
		ParentObjectID: "WorkItem", ParentRecordID: "parent-1", ResolverID: "document_publication",
		Sources: []semantic.Source{{SourceFieldID: "DocumentLink", SourceValue: "ABCDEF123456", SourceValueOffset: 7}},
	}
	cases := []struct {
		name     string
		engine   *runtimeAttachmentSourceEngineStub
		mutate   func(*semantic.ResolveRequest)
		readWant int
	}{
		{
			name: "offset proves different bytes",
			engine: &runtimeAttachmentSourceEngineStub{readable: map[string]bool{"DocumentLink": true}, rows: map[string]models.SObject{
				"WorkItem/parent-1": {constants.FieldID: "parent-1", "DocumentLink": "before/DIFFERENT123/after"},
			}},
			readWant: 1,
		},
		{
			name: "offset out of range",
			engine: &runtimeAttachmentSourceEngineStub{readable: map[string]bool{"DocumentLink": true}, rows: map[string]models.SObject{
				"WorkItem/parent-1": {constants.FieldID: "parent-1", "DocumentLink": "before/ABCDEF123456/after"},
			}},
			mutate:   func(request *semantic.ResolveRequest) { request.Sources[0].SourceValueOffset = 999 },
			readWant: 1,
		},
		{
			name:     "FLS hidden field",
			engine:   &runtimeAttachmentSourceEngineStub{readable: map[string]bool{"DocumentLink": false}},
			readWant: 0,
		},
		{
			name: "FLS filtered parent field",
			engine: &runtimeAttachmentSourceEngineStub{readable: map[string]bool{"DocumentLink": true}, rows: map[string]models.SObject{
				"WorkItem/parent-1": {constants.FieldID: "parent-1"},
			}},
			readWant: 1,
		},
		{
			name:     "missing or RLS masked parent",
			engine:   &runtimeAttachmentSourceEngineStub{readable: map[string]bool{"DocumentLink": true}},
			readWant: 1,
		},
		{
			name:   "unbound source field",
			engine: &runtimeAttachmentSourceEngineStub{readable: map[string]bool{"DocumentLink": true}},
			mutate: func(request *semantic.ResolveRequest) {
				request.Sources[0].SourceFieldID = "OtherField"
			},
			readWant: 0,
		},
	}
	for _, test := range cases {
		t.Run(test.name, func(t *testing.T) {
			request := request
			if test.mutate != nil {
				test.mutate(&request)
			}
			resolver := &runtimeAttachmentSourceResolverStub{}
			service := runtimeAttachmentSourceService(t, view, test.engine, resolver)
			ctx, cancel := context.WithTimeout(context.Background(), time.Second)
			defer cancel()
			result, err := service.ResolveAttachmentSources(ctx, request, &models.UserSession{ID: "user-1"})
			if err != nil || !reflect.DeepEqual(result, []semantic.SourceResult{{Index: 0, Outcome: semantic.SourceUnavailable}}) ||
				resolver.calls != 0 || test.engine.retrieveCalls != test.readWant {
				t.Fatalf("result=%#v err=%v resolver=%d reads=%d", result, err, resolver.calls, test.engine.retrieveCalls)
			}
		})
	}
}

func TestRuntimeAttachmentSourcesKeepsProofFailuresOutOfTheBatchLookup(t *testing.T) {
	view := runtimeAttachmentSourceView(t)
	engine := &runtimeAttachmentSourceEngineStub{
		readable: map[string]bool{"DocumentLink": true},
		rows: map[string]models.SObject{
			"WorkItem/parent-1": {constants.FieldID: "parent-1", "DocumentLink": "before/ABCDEF123456/after"},
		},
	}
	resolver := &runtimeAttachmentSourceResolverStub{results: []semantic.SourceResult{{Index: 0, Outcome: semantic.SourceNotFound}}}
	service := runtimeAttachmentSourceService(t, view, engine, resolver)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	result, err := service.ResolveAttachmentSources(ctx, semantic.ResolveRequest{
		ParentObjectID: "WorkItem", ParentRecordID: "parent-1", ResolverID: "document_publication",
		Sources: []semantic.Source{
			{SourceFieldID: "DocumentLink", SourceValue: "ABCDEF123456", SourceValueOffset: 7},
			{SourceFieldID: "DocumentLink", SourceValue: "guessed", SourceValueOffset: 0},
		},
	}, &models.UserSession{ID: "user-1"})
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(resolver.sourceValues, []string{"ABCDEF123456"}) ||
		!reflect.DeepEqual(result, []semantic.SourceResult{{Index: 0, Outcome: semantic.SourceNotFound}, {Index: 1, Outcome: semantic.SourceUnavailable}}) {
		t.Fatalf("result=%#v resolver=%#v", result, resolver)
	}
}

func TestRuntimeAttachmentSourcesRejectsMalformedResolverResults(t *testing.T) {
	view := runtimeAttachmentSourceView(t)
	engine := &runtimeAttachmentSourceEngineStub{readable: map[string]bool{"DocumentLink": true}, rows: map[string]models.SObject{
		"WorkItem/parent-1": {constants.FieldID: "parent-1", "DocumentLink": "before/ABCDEF123456/after"},
	}}
	resolver := &runtimeAttachmentSourceResolverStub{results: []semantic.SourceResult{{Index: 1, Outcome: semantic.SourceNotFound}}}
	service := runtimeAttachmentSourceService(t, view, engine, resolver)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	_, err := service.ResolveAttachmentSources(ctx, semantic.ResolveRequest{
		ParentObjectID: "WorkItem", ParentRecordID: "parent-1", ResolverID: "document_publication",
		Sources: []semantic.Source{{SourceFieldID: "DocumentLink", SourceValue: "ABCDEF123456", SourceValueOffset: 7}},
	}, &models.UserSession{ID: "user-1"})
	var failure *semantic.Failure
	if !errors.As(err, &failure) || failure.Category != semantic.FailureInternal {
		t.Fatalf("error = %v, want internal", err)
	}
}

func TestRuntimeAttachmentSourcesPropagatesCancellationFromMaskedDependencies(t *testing.T) {
	request := semantic.ResolveRequest{
		ParentObjectID: "WorkItem", ParentRecordID: "parent-1", ResolverID: "document_publication",
		Sources: []semantic.Source{{SourceFieldID: "DocumentLink", SourceValue: "ABCDEF123456", SourceValueOffset: 7}},
	}
	parent := models.SObject{constants.FieldID: "parent-1", "DocumentLink": "before/ABCDEF123456/after"}
	for _, tc := range []struct {
		name     string
		wantErr  error
		engine   *runtimeAttachmentSourceEngineStub
		resolver *runtimeAttachmentSourceResolverStub
	}{
		{
			name:    "field readability cancellation",
			wantErr: context.Canceled,
			engine: &runtimeAttachmentSourceEngineStub{
				readableErr: context.Canceled,
			},
			resolver: &runtimeAttachmentSourceResolverStub{},
		},
		{
			name:    "parent retrieval deadline",
			wantErr: context.DeadlineExceeded,
			engine: &runtimeAttachmentSourceEngineStub{
				readable: map[string]bool{"DocumentLink": true}, retrieveErr: context.DeadlineExceeded,
			},
			resolver: &runtimeAttachmentSourceResolverStub{},
		},
		{
			name:    "files continuation cancellation",
			wantErr: context.Canceled,
			engine: &runtimeAttachmentSourceEngineStub{
				readable: map[string]bool{"DocumentLink": true}, rows: map[string]models.SObject{"WorkItem/parent-1": parent},
			},
			resolver: &runtimeAttachmentSourceResolverStub{err: context.Canceled},
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			service := runtimeAttachmentSourceService(t, runtimeAttachmentSourceView(t), tc.engine, tc.resolver)
			result, err := service.ResolveAttachmentSources(context.Background(), request, &models.UserSession{ID: "user-1"})
			if result != nil || !errors.Is(err, tc.wantErr) {
				t.Fatalf("result=%#v err=%v, want no result and %v", result, err, tc.wantErr)
			}
		})
	}
}

func TestRuntimeAttachmentSourcesCancellationWinsAtDependencyReturn(t *testing.T) {
	request := semantic.ResolveRequest{
		ParentObjectID: "WorkItem", ParentRecordID: "parent-1", ResolverID: "document_publication",
		Sources: []semantic.Source{{SourceFieldID: "DocumentLink", SourceValue: "ABCDEF123456", SourceValueOffset: 7}},
	}
	parent := models.SObject{constants.FieldID: "parent-1", "DocumentLink": "before/ABCDEF123456/after"}
	for _, tc := range []struct {
		name      string
		configure func(cancel context.CancelFunc) (*runtimeAttachmentSourceEngineStub, *runtimeAttachmentSourceResolverStub)
	}{
		{
			name: "field readability",
			configure: func(cancel context.CancelFunc) (*runtimeAttachmentSourceEngineStub, *runtimeAttachmentSourceResolverStub) {
				return &runtimeAttachmentSourceEngineStub{readable: map[string]bool{"DocumentLink": false}, fieldReadableBeforeReturn: cancel}, &runtimeAttachmentSourceResolverStub{}
			},
		},
		{
			name: "parent retrieval",
			configure: func(cancel context.CancelFunc) (*runtimeAttachmentSourceEngineStub, *runtimeAttachmentSourceResolverStub) {
				return &runtimeAttachmentSourceEngineStub{readable: map[string]bool{"DocumentLink": true}, retrieveBeforeReturn: cancel}, &runtimeAttachmentSourceResolverStub{}
			},
		},
		{
			name: "files continuation",
			configure: func(cancel context.CancelFunc) (*runtimeAttachmentSourceEngineStub, *runtimeAttachmentSourceResolverStub) {
				return &runtimeAttachmentSourceEngineStub{readable: map[string]bool{"DocumentLink": true}, rows: map[string]models.SObject{"WorkItem/parent-1": parent}}, &runtimeAttachmentSourceResolverStub{
					results: []semantic.SourceResult{{Index: 0, Outcome: semantic.SourceNotFound}}, beforeReturn: cancel,
				}
			},
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			ctx, cancel := context.WithCancel(context.Background())
			defer cancel()
			engine, resolver := tc.configure(cancel)
			service := runtimeAttachmentSourceService(t, runtimeAttachmentSourceView(t), engine, resolver)
			result, err := service.ResolveAttachmentSources(ctx, request, &models.UserSession{ID: "user-1"})
			if result != nil || !errors.Is(err, context.Canceled) {
				t.Fatalf("result=%#v err=%v, want no result and canceled", result, err)
			}
		})
	}
}

type runtimeAttachmentSourceEngineStub struct {
	rows                      map[string]models.SObject
	readable                  map[string]bool
	retrieveErr               error
	readableErr               error
	retrieveBeforeReturn      func()
	fieldReadableBeforeReturn func()
	retrieveCalls             int
}

func (s *runtimeAttachmentSourceEngineStub) Retrieve(_ context.Context, objectID, recordID string, _ *models.UserSession) (models.SObject, error) {
	s.retrieveCalls++
	if s.retrieveBeforeReturn != nil {
		s.retrieveBeforeReturn()
	}
	return s.rows[objectID+"/"+recordID], s.retrieveErr
}

func (s *runtimeAttachmentSourceEngineStub) FieldReadable(_ context.Context, _, fieldID string, _ *models.UserSession) (bool, error) {
	if s.fieldReadableBeforeReturn != nil {
		s.fieldReadableBeforeReturn()
	}
	return s.readable[fieldID], s.readableErr
}

type runtimeAttachmentSourceMetadataStub struct {
	view *catalogsnapshot.RuntimeMetadataView
}

func (s runtimeAttachmentSourceMetadataStub) BoundRuntimeMetadataView(context.Context) (*catalogsnapshot.RuntimeMetadataView, bool) {
	return s.view, s.view != nil
}

type runtimeAttachmentSourceResolverStub struct {
	results        []semantic.SourceResult
	calls          int
	parentObjectID string
	parentRecordID string
	sourceValues   []string
	err            error
	beforeReturn   func()
}

func (s *runtimeAttachmentSourceResolverStub) ResolveAttachmentSources(_ context.Context, _ *catalogsnapshot.RuntimeMetadataView, parentObjectID, parentRecordID string, _ catalogsnapshot.RecordAttachmentSourceResolver, sourceValues []string, _ *models.UserSession) ([]semantic.SourceResult, error) {
	s.calls++
	s.parentObjectID = parentObjectID
	s.parentRecordID = parentRecordID
	s.sourceValues = append([]string(nil), sourceValues...)
	if s.beforeReturn != nil {
		s.beforeReturn()
	}
	return s.results, s.err
}

func runtimeAttachmentSourceService(t *testing.T, view *catalogsnapshot.RuntimeMetadataView, engine RuntimeAttachmentSourceEngine, resolver RuntimeAttachmentSourceResolver) *RuntimeAttachmentSourceService {
	t.Helper()
	service, err := NewRuntimeAttachmentSourceService(RuntimeAttachmentSourceConfig{
		Engine: engine, Metadata: runtimeAttachmentSourceMetadataStub{view: view}, Resolver: resolver,
	})
	if err != nil {
		t.Fatal(err)
	}
	return service
}

func runtimeAttachmentSourceView(t *testing.T) *catalogsnapshot.RuntimeMetadataView {
	t.Helper()
	const revision catalogsnapshot.CatalogRevision = 1
	start := uint16(3)
	workItem := &models.ObjectMetadata{ID: "work-item", APIName: "WorkItem", Label: "Work Item", PluralLabel: "Work Items", SharingModel: constants.SharingModelPublicReadWrite, Fields: []models.FieldMetadata{
		{ID: "wi-id", APIName: constants.FieldID, Label: "ID", Type: constants.FieldTypeString, PhysicalName: "id", IsSystem: true},
		{ID: "wi-link", APIName: "DocumentLink", Label: "Document Link", Type: constants.FieldTypeRichtextarea, PhysicalName: "document_link"},
		{ID: "wi-publication", APIName: "DocumentPublicationId", Label: "Document Publication", Type: constants.FieldTypeReference, PhysicalName: "document_publication_id", ReferenceTo: []string{"DocumentPublication"}},
	}}
	publication := &models.ObjectMetadata{ID: "publication", APIName: "DocumentPublication", Label: "Publication", PluralLabel: "Publications", SharingModel: constants.SharingModelPublicReadWrite, Fields: []models.FieldMetadata{
		{ID: "pub-id", APIName: constants.FieldID, Label: "ID", Type: constants.FieldTypeString, PhysicalName: "id", IsSystem: true},
		{ID: "pub-name", APIName: "Name", Label: "Name", Type: constants.FieldTypeString, PhysicalName: "name", IsNameField: true},
		{ID: "pub-version", APIName: "DocumentVersionId", Label: "Version", Type: constants.FieldTypeString, PhysicalName: "document_version_id"},
		{ID: "pub-deleted", APIName: constants.FieldIsDeleted, Label: "Deleted", Type: constants.FieldTypeBoolean, PhysicalName: "is_deleted"},
	}}
	artifact, err := catalogsnapshot.CompileCatalogSnapshot(catalogsnapshot.CompileInput{
		OrgID: "org-record-view", Lineage: catalogsnapshot.Lineage{CatalogRevision: revision, SchemaGeneration: 1, EngineGeneration: 1},
		Objects: []catalogsnapshot.ObjectInput{{CatalogRevision: revision, Metadata: publication}, {CatalogRevision: revision, Metadata: workItem}},
		AttachmentSourceResolvers: []catalogsnapshot.RecordAttachmentSourceResolverInput{{
			CatalogRevision: revision, ResolverID: "document_publication", Kind: catalogsnapshot.RecordAttachmentSourceByRecordV2,
			SourceObjectAPIName: "DocumentPublication", SourceFileReferenceFieldAPIName: "DocumentVersionId", SourceDeletedFieldAPIName: constants.FieldIsDeleted,
			SourceRecordMatcher: &catalogsnapshot.RecordAttachmentSourceRecordMatcher{Kind: catalogsnapshot.RecordAttachmentSourceMatchRecordIDSegmentV2, StartOffsetBytes: &start, LengthBytes: 12},
		}},
		AttachmentSourceFields: []catalogsnapshot.RecordAttachmentSourceFieldInput{{CatalogRevision: revision, ObjectAPIName: "WorkItem", FieldAPIName: "DocumentLink", ResolverID: "document_publication"}},
		Projections:            []catalogsnapshot.RuntimeProjectionInput{{CatalogRevision: revision, AppInstallationID: "installation-record-view", CapabilityID: semantic.CapabilityName, Profile: catalogsnapshot.ProjectionRecordViewV1, ObjectAPINames: []string{"DocumentPublication", "WorkItem"}}},
	})
	if err != nil {
		t.Fatal(err)
	}
	snapshot, err := catalogsnapshot.OpenCatalogSnapshot(artifact)
	if err != nil {
		t.Fatal(err)
	}
	projection, err := catalogsnapshot.ProjectRuntimeMetadataView(snapshot, catalogsnapshot.ProjectionKey{AppInstallationID: "installation-record-view", CapabilityID: semantic.CapabilityName})
	if err != nil {
		t.Fatal(err)
	}
	view, err := catalogsnapshot.OpenRuntimeMetadataView(projection)
	if err != nil {
		t.Fatal(err)
	}
	return view
}

func runtimeRecordTypeReferenceView(t *testing.T) *catalogsnapshot.RuntimeMetadataView {
	t.Helper()
	const revision catalogsnapshot.CatalogRevision = 1
	relationship := constants.ObjectRecordType
	workItem := &models.ObjectMetadata{ID: "work-item", APIName: "WorkItem", Label: "Work Item", PluralLabel: "Work Items", SharingModel: constants.SharingModelPublicReadWrite, Fields: []models.FieldMetadata{
		{ID: "wi-id", APIName: constants.FieldID, Label: "ID", Type: constants.FieldTypeString, PhysicalName: "id", IsSystem: true},
		{ID: "wi-record-type", APIName: constants.FieldRecordTypeID, Label: "Record Type", Type: constants.FieldTypeReference, PhysicalName: "record_type_id", ReferenceTo: []string{constants.ObjectRecordType}, RelationshipName: &relationship},
	}}
	recordType := &models.ObjectMetadata{ID: "record-type", APIName: constants.ObjectRecordType, Label: "Record Type", PluralLabel: "Record Types", SharingModel: constants.SharingModelPublicReadWrite}
	artifact, err := catalogsnapshot.CompileCatalogSnapshot(catalogsnapshot.CompileInput{
		OrgID: "org-record-view", Lineage: catalogsnapshot.Lineage{CatalogRevision: revision, SchemaGeneration: 1, EngineGeneration: 1},
		Objects:     []catalogsnapshot.ObjectInput{{CatalogRevision: revision, Metadata: recordType}, {CatalogRevision: revision, Metadata: workItem}},
		Projections: []catalogsnapshot.RuntimeProjectionInput{{CatalogRevision: revision, AppInstallationID: "installation-record-view", CapabilityID: semantic.CapabilityName, Profile: catalogsnapshot.ProjectionRecordViewV1, ObjectAPINames: []string{constants.ObjectRecordType, "WorkItem"}}},
	})
	if err != nil {
		t.Fatal(err)
	}
	snapshot, err := catalogsnapshot.OpenCatalogSnapshot(artifact)
	if err != nil {
		t.Fatal(err)
	}
	projection, err := catalogsnapshot.ProjectRuntimeMetadataView(snapshot, catalogsnapshot.ProjectionKey{AppInstallationID: "installation-record-view", CapabilityID: semantic.CapabilityName})
	if err != nil {
		t.Fatal(err)
	}
	view, err := catalogsnapshot.OpenRuntimeMetadataView(projection)
	if err != nil {
		t.Fatal(err)
	}
	return view
}
