package catalogapp

import (
	"context"
	"crypto/ed25519"
	"crypto/sha256"
	"errors"
	"fmt"
	"regexp"
	"sort"
	"strings"
	"testing"
	"time"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/appauthority"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/appidentitykeydeclarationv1"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/layoutbundleslotv1"
	"github.com/pingcap-inc/nexuscrm-controlplane/internal/catalogadmissionfence"
	"github.com/pingcap-inc/nexuscrm-controlplane/releaseartifact"
)

func TestImportReleaseArtifactRejectsFuturePublisherTimeBeforeTrustLookup(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()
	now := time.Unix(1_800_000_000, 0).UTC()
	authority, err := New(db, Options{Now: func() time.Time { return now }})
	if err != nil {
		t.Fatal(err)
	}
	packageRaw, attestationRaw := testImportArtifact(t, now.Add(time.Microsecond))
	_, err = authority.ImportReleaseArtifact(context.Background(), packageRaw, attestationRaw, releaseartifact.HostCompatibility{
		Protocols: []releaseartifact.SupportedProtocol{}, EngineCapabilities: []string{"runtime.records.read.v1"},
		ComponentKinds: []releaseartifact.SupportedComponentKind{{ComponentKind: "metadata.object.v1", ComponentFormatVersion: 1}},
	})
	if err == nil || !strings.Contains(err.Error(), "later than Catalog verification time") {
		t.Fatalf("future publisher time error = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatal(err)
	}
}

func TestVerifyReleaseCandidateIsPureAndBindsCompleteOperatorTuple(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()
	now := time.Unix(1_800_000_000, 0).UTC()
	authority, err := New(db, Options{Now: func() time.Time { return now }})
	if err != nil {
		t.Fatal(err)
	}
	packageRaw, attestationRaw := testImportArtifact(t, now.Add(-time.Second))
	trust := PublisherTrust{PublisherID: "io.example", Active: true, Keys: []PublisherKey{{
		KeyID: "release-key-1", Algorithm: releaseartifact.SignatureAlgorithm, PublicKey: testImportPublicKey(),
		ValidFrom: now.Add(-time.Hour), ValidUntil: now.Add(time.Hour),
	}}}
	host := releaseartifact.HostCompatibility{
		Protocols: []releaseartifact.SupportedProtocol{}, EngineCapabilities: []string{"runtime.records.read.v1"},
		ComponentKinds: []releaseartifact.SupportedComponentKind{{ComponentKind: "metadata.object.v1", ComponentFormatVersion: 1}},
	}
	release, err := authority.VerifyReleaseCandidate(context.Background(), packageRaw, attestationRaw, trust, "io.example.crm", host)
	if err != nil {
		t.Fatalf("verify release candidate: %v", err)
	}
	if release.AppID != "io.example.crm" || release.PublisherID != "io.example" || release.SemanticVersion != "1.0.0" || release.AppReleaseDigest == "" || release.MetadataBundleDigest == "" {
		t.Fatalf("incomplete verified release candidate: %#v", release)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("candidate verification accessed Catalog storage: %v", err)
	}

	if _, err := authority.VerifyReleaseCandidate(context.Background(), packageRaw, attestationRaw, trust, "io.other.app", host); err == nil || !strings.Contains(err.Error(), "does not match operator-authorized App") {
		t.Fatalf("wrong expected App error = %v", err)
	}
	wrongPublisher := trust
	wrongPublisher.PublisherID = "io.other"
	if _, err := authority.VerifyReleaseCandidate(context.Background(), packageRaw, attestationRaw, wrongPublisher, "io.example.crm", host); err == nil || !strings.Contains(err.Error(), "publisher does not match") {
		t.Fatalf("wrong publisher error = %v", err)
	}
	incompatible := host
	incompatible.EngineCapabilities = []string{}
	if _, err := authority.VerifyReleaseCandidate(context.Background(), packageRaw, attestationRaw, trust, "io.example.crm", incompatible); err == nil {
		t.Fatal("incompatible release candidate was accepted")
	}
	attestation, err := releaseartifact.ParseAttestation(attestationRaw)
	if err != nil {
		t.Fatal(err)
	}
	attestation.Signature[0] ^= 0xff
	tamperedAttestation, err := releaseartifact.MarshalAttestation(attestation)
	if err != nil {
		t.Fatal(err)
	}
	if _, err := authority.VerifyReleaseCandidate(context.Background(), packageRaw, tamperedAttestation, trust, "io.example.crm", host); err == nil || !strings.Contains(err.Error(), "signature verification failed") {
		t.Fatalf("tampered signature error = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("rejected candidate accessed Catalog storage: %v", err)
	}
}

func TestVerifyReleaseCandidateRejectsMalformedRegisteredSlotBeforeCatalogMutation(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()
	now := time.Unix(1_800_000_000, 0).UTC()
	authority, err := New(db, Options{Now: func() time.Time { return now }})
	if err != nil {
		t.Fatal(err)
	}
	packageRaw, attestationRaw := testMalformedSlotImportArtifact(t, now.Add(-time.Second))
	trust := PublisherTrust{PublisherID: "io.example", Active: true, Keys: []PublisherKey{{
		KeyID: "release-key-1", Algorithm: releaseartifact.SignatureAlgorithm, PublicKey: testImportPublicKey(),
		ValidFrom: now.Add(-time.Hour), ValidUntil: now.Add(time.Hour),
	}}}
	host := releaseartifact.HostCompatibility{
		Protocols: []releaseartifact.SupportedProtocol{}, EngineCapabilities: []string{"runtime.records.read.v1"},
		ComponentKinds: []releaseartifact.SupportedComponentKind{
			{ComponentKind: "metadata.object.v1", ComponentFormatVersion: 1},
			{ComponentKind: layoutbundleslotv1.ComponentKind, ComponentFormatVersion: layoutbundleslotv1.ComponentFormatVersion},
		},
	}
	_, err = authority.VerifyReleaseCandidate(context.Background(), packageRaw, attestationRaw, trust, "io.example.crm", host)
	if err == nil || !strings.Contains(err.Error(), "layout-bundle slot component") {
		t.Fatalf("malformed registered slot error = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("malformed candidate accessed Catalog storage: %v", err)
	}
}

func TestVerifyReleaseCandidateValidatesIdentityKeyDeclarationsBeforeCatalogMutation(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()
	now := time.Unix(1_800_000_000, 0).UTC()
	authority, err := New(db, Options{Now: func() time.Time { return now }})
	if err != nil {
		t.Fatal(err)
	}
	trust := PublisherTrust{PublisherID: "io.example", Active: true, Keys: []PublisherKey{{
		KeyID: "release-key-1", Algorithm: releaseartifact.SignatureAlgorithm, PublicKey: testImportPublicKey(),
		ValidFrom: now.Add(-time.Hour), ValidUntil: now.Add(time.Hour),
	}}}
	host := releaseartifact.HostCompatibility{
		Protocols: []releaseartifact.SupportedProtocol{}, EngineCapabilities: []string{},
		ComponentKinds: []releaseartifact.SupportedComponentKind{
			{ComponentKind: appidentitykeydeclarationv1.ProfileComponentKind, ComponentFormatVersion: appidentitykeydeclarationv1.ComponentFormatVersion},
			{ComponentKind: appidentitykeydeclarationv1.RoleComponentKind, ComponentFormatVersion: appidentitykeydeclarationv1.ComponentFormatVersion},
		},
	}
	valid := testIdentityKeyDeclarationPackage(t, false)
	packageRaw, attestationRaw := testSignedImportPackage(t, valid, now.Add(-time.Second))
	candidate, err := authority.VerifyReleaseCandidate(context.Background(), packageRaw, attestationRaw, trust, valid.Manifest.AppID, host)
	if err != nil || candidate.AppReleaseDigest != valid.AppReleaseDigest {
		t.Fatalf("valid identity declaration candidate = %#v, %v", candidate, err)
	}
	malformed := testIdentityKeyDeclarationPackage(t, true)
	packageRaw, attestationRaw = testSignedImportPackage(t, malformed, now.Add(-time.Second))
	if _, err := authority.VerifyReleaseCandidate(context.Background(), packageRaw, attestationRaw, trust, malformed.Manifest.AppID, host); err == nil || !strings.Contains(err.Error(), "Profile key declaration") {
		t.Fatalf("malformed identity declaration candidate = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("identity declaration candidate accessed Catalog storage: %v", err)
	}
}

func TestImportReleaseArtifactRejectsTamperedSignatureBeforeRegistration(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()
	now := time.Unix(1_800_000_000, 0).UTC()
	authority, err := New(db, Options{Now: func() time.Time { return now }})
	if err != nil {
		t.Fatal(err)
	}
	packageRaw, attestationRaw := testImportArtifact(t, now.Add(-time.Second))
	attestation, err := releaseartifact.ParseAttestation(attestationRaw)
	if err != nil {
		t.Fatal(err)
	}
	attestation.Signature[0] ^= 0xff
	tamperedAttestation, err := releaseartifact.MarshalAttestation(attestation)
	if err != nil {
		t.Fatal(err)
	}
	mock.ExpectQuery(regexp.QuoteMeta(`SELECT p.State, k.State, k.Algorithm, k.PublicKey, k.ValidFrom, k.ValidUntil
FROM _System_CatalogPublisher p
JOIN _System_CatalogPublisherKey k ON k.PublisherId = p.PublisherId
WHERE p.PublisherId = ? AND k.KeyId = ?`)).
		WithArgs("io.example", "release-key-1").
		WillReturnRows(sqlmock.NewRows([]string{
			"State", "State", "Algorithm", "PublicKey", "ValidFrom", "ValidUntil",
		}).AddRow("ACTIVE", "ACTIVE", releaseartifact.SignatureAlgorithm, testImportPublicKey(), now.Add(-time.Hour), now.Add(time.Hour)))
	_, err = authority.ImportReleaseArtifact(context.Background(), packageRaw, tamperedAttestation, testImportHost())
	if err == nil || !strings.Contains(err.Error(), "signature verification failed") {
		t.Fatalf("tampered artifact import = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("tampered artifact entered registration: %v", err)
	}
}

func TestImportReleaseArtifactRejectsSigningKeyChangedAfterVerification(t *testing.T) {
	now := time.Unix(1_800_000_000, 0).UTC()
	for _, testCase := range []struct {
		name           string
		publisherState string
		keyState       string
		publicKey      []byte
		validUntil     time.Time
	}{
		{
			name:           "different public key",
			publisherState: "ACTIVE",
			keyState:       "ACTIVE",
			publicKey:      alternateImportPublicKey(),
			validUntil:     now.Add(time.Hour),
		},
		{
			name:           "publisher revoked",
			publisherState: "REVOKED",
			keyState:       "ACTIVE",
			publicKey:      testImportPublicKey(),
			validUntil:     now.Add(time.Hour),
		},
		{
			name:           "key revoked",
			publisherState: "ACTIVE",
			keyState:       "REVOKED",
			publicKey:      testImportPublicKey(),
			validUntil:     now.Add(time.Hour),
		},
		{
			name:           "validity window changed",
			publisherState: "ACTIVE",
			keyState:       "ACTIVE",
			publicKey:      testImportPublicKey(),
			validUntil:     now.Add(2 * time.Hour),
		},
	} {
		t.Run(testCase.name, func(t *testing.T) {
			db, mock, err := sqlmock.New()
			if err != nil {
				t.Fatal(err)
			}
			defer db.Close()
			authority, err := New(db, Options{Now: func() time.Time { return now }})
			if err != nil {
				t.Fatal(err)
			}
			packageRaw, attestationRaw := testImportArtifact(t, now.Add(-time.Second))
			mock.ExpectQuery(regexp.QuoteMeta(`SELECT p.State, k.State, k.Algorithm, k.PublicKey, k.ValidFrom, k.ValidUntil
FROM _System_CatalogPublisher p
JOIN _System_CatalogPublisherKey k ON k.PublisherId = p.PublisherId
WHERE p.PublisherId = ? AND k.KeyId = ?`)).
				WithArgs("io.example", "release-key-1").
				WillReturnRows(sqlmock.NewRows([]string{
					"State", "State", "Algorithm", "PublicKey", "ValidFrom", "ValidUntil",
				}).AddRow("ACTIVE", "ACTIVE", releaseartifact.SignatureAlgorithm, testImportPublicKey(), now.Add(-time.Hour), now.Add(time.Hour)))
			mock.ExpectBegin()
			mock.ExpectQuery(regexp.QuoteMeta(`SELECT FenceKey, FenceGeneration
FROM _System_CatalogPublicationAdmissionFence
WHERE FenceKey = ? FOR UPDATE`)).
				WithArgs(catalogadmissionfence.Key).
				WillReturnRows(sqlmock.NewRows([]string{"FenceKey", "FenceGeneration"}).AddRow(catalogadmissionfence.Key, []byte("0")))
			mock.ExpectQuery(regexp.QuoteMeta(`SELECT PublisherId FROM _System_CatalogAppIdentity WHERE AppId = ? FOR UPDATE`)).
				WithArgs("io.example.crm").
				WillReturnRows(sqlmock.NewRows([]string{"PublisherId"}).AddRow("io.example"))
			mock.ExpectQuery(regexp.QuoteMeta(`SELECT p.State, k.State, k.Algorithm, k.PublicKey, k.ValidFrom, k.ValidUntil
FROM _System_CatalogPublisher p
JOIN _System_CatalogPublisherKey k ON k.PublisherId = p.PublisherId
WHERE p.PublisherId = ? AND k.KeyId = ? FOR UPDATE`)).
				WithArgs("io.example", "release-key-1").
				WillReturnRows(sqlmock.NewRows([]string{
					"State", "State", "Algorithm", "PublicKey", "ValidFrom", "ValidUntil",
				}).AddRow(testCase.publisherState, testCase.keyState, releaseartifact.SignatureAlgorithm, testCase.publicKey, now.Add(-time.Hour), testCase.validUntil))
			mock.ExpectRollback()

			_, err = authority.ImportReleaseArtifact(context.Background(), packageRaw, attestationRaw, testImportHost())
			if !errors.Is(err, ErrConflict) {
				t.Fatalf("import after signing-key %s = %v, want ErrConflict", testCase.name, err)
			}
			if err := mock.ExpectationsWereMet(); err != nil {
				t.Fatalf("signing-key %s reached release mutation: %v", testCase.name, err)
			}
		})
	}
}

func testImportHost() releaseartifact.HostCompatibility {
	return releaseartifact.HostCompatibility{
		Protocols:          []releaseartifact.SupportedProtocol{},
		EngineCapabilities: []string{"runtime.records.read.v1"},
		ComponentKinds: []releaseartifact.SupportedComponentKind{{
			ComponentKind: "metadata.object.v1", ComponentFormatVersion: 1,
		}},
	}
}

func testImportArtifact(t *testing.T, issuedAt time.Time) ([]byte, []byte) {
	t.Helper()
	behavior := []byte(`{"fields":["Subject"]}`)
	kind := "metadata.object.v1"
	digest, err := appauthority.ComponentDigest(kind, 1, behavior)
	if err != nil {
		t.Fatal(err)
	}
	component := appauthority.Component{ComponentKind: kind, ComponentFormatVersion: 1, NamespaceID: "io.example.crm",
		BusinessKey: "Case", DefinitionVersion: "1", LocalDependencies: []string{}, ExternalComponentRefs: []appauthority.ExternalComponentRef{},
		OverridePolicy: appauthority.OverridePolicyReplace, RemovalPolicy: appauthority.RemovalPolicyBlockIfReferencedOrOverridden, ContentDigest: digest}
	component.ComponentID = appauthority.ComponentID(component.NamespaceID, component.ComponentKind, component.BusinessKey)
	bundle := appauthority.MetadataBundle{SchemaVersion: appauthority.MetadataBundleSchemaVersion, Components: []appauthority.Component{component}}
	manifest := appauthority.AppManifest{SchemaVersion: appauthority.AppManifestSchemaVersion, AppID: "io.example.crm", PublisherID: "io.example",
		SemanticVersion: "1.0.0", MetadataBundleDigest: "sha256:" + strings.Repeat("0", 64), Dependencies: []appauthority.Dependency{},
		OwnedNamespaces: []string{"io.example.crm"}, RequiredProtocols: []appauthority.ProtocolRequirement{},
		RequiredEngineCapabilities: []string{"runtime.records.read.v1"}, ProvidedCapabilities: []appauthority.ProvidedCapability{},
		FrontendCompatibility: appauthority.FrontendCompatibility{Mode: appauthority.FrontendModeNone}}
	manifest.MetadataBundleDigest, err = appauthority.MetadataBundleDigest(bundle, manifest)
	if err != nil {
		t.Fatal(err)
	}
	pkg, err := appauthority.VerifyPackage(appauthority.Package{Manifest: manifest, Bundle: bundle,
		ComponentBlobs: []appauthority.ComponentBlob{{ComponentID: component.ComponentID, CanonicalBehavior: behavior}}})
	if err != nil {
		t.Fatal(err)
	}
	return testSignedImportPackage(t, pkg, issuedAt)
}

func testMalformedSlotImportArtifact(t *testing.T, issuedAt time.Time) ([]byte, []byte) {
	t.Helper()
	targetBehavior := []byte(`{"fields":["Subject"]}`)
	targetDigest, err := appauthority.ComponentDigest("metadata.object.v1", 1, targetBehavior)
	if err != nil {
		t.Fatal(err)
	}
	target := appauthority.Component{ComponentKind: "metadata.object.v1", ComponentFormatVersion: 1, NamespaceID: "io.example.crm",
		BusinessKey: "Case", DefinitionVersion: "1", LocalDependencies: []string{}, ExternalComponentRefs: []appauthority.ExternalComponentRef{},
		OverridePolicy: appauthority.OverridePolicyReplace, RemovalPolicy: appauthority.RemovalPolicyBlockIfReferencedOrOverridden, ContentDigest: targetDigest}
	target.ComponentID = appauthority.ComponentID(target.NamespaceID, target.ComponentKind, target.BusinessKey)
	malformedBehavior := []byte(`{}`)
	slotDigest, err := appauthority.ComponentDigest(layoutbundleslotv1.ComponentKind, layoutbundleslotv1.ComponentFormatVersion, malformedBehavior)
	if err != nil {
		t.Fatal(err)
	}
	slot := appauthority.Component{ComponentKind: layoutbundleslotv1.ComponentKind, ComponentFormatVersion: layoutbundleslotv1.ComponentFormatVersion,
		NamespaceID: "io.example.crm", BusinessKey: "key256:" + strings.Repeat("1", 64), DefinitionVersion: "1",
		LocalDependencies: []string{target.ComponentID}, ExternalComponentRefs: []appauthority.ExternalComponentRef{},
		OverridePolicy: appauthority.OverridePolicyReplace, RemovalPolicy: appauthority.RemovalPolicyBlockIfReferencedOrOverridden, ContentDigest: slotDigest}
	slot.ComponentID = appauthority.ComponentID(slot.NamespaceID, slot.ComponentKind, slot.BusinessKey)
	type componentBlob struct {
		component appauthority.Component
		behavior  []byte
	}
	values := []componentBlob{{component: target, behavior: targetBehavior}, {component: slot, behavior: malformedBehavior}}
	sort.Slice(values, func(left, right int) bool {
		return values[left].component.ComponentID < values[right].component.ComponentID
	})
	bundle := appauthority.MetadataBundle{SchemaVersion: appauthority.MetadataBundleSchemaVersion, Components: make([]appauthority.Component, len(values))}
	blobs := make([]appauthority.ComponentBlob, len(values))
	for index, value := range values {
		bundle.Components[index] = value.component
		blobs[index] = appauthority.ComponentBlob{ComponentID: value.component.ComponentID, CanonicalBehavior: value.behavior}
	}
	manifest := appauthority.AppManifest{SchemaVersion: appauthority.AppManifestSchemaVersion, AppID: "io.example.crm", PublisherID: "io.example",
		SemanticVersion: "1.0.0", MetadataBundleDigest: "sha256:" + strings.Repeat("0", 64), Dependencies: []appauthority.Dependency{},
		OwnedNamespaces: []string{"io.example.crm"}, RequiredProtocols: []appauthority.ProtocolRequirement{},
		RequiredEngineCapabilities: []string{"runtime.records.read.v1"}, ProvidedCapabilities: []appauthority.ProvidedCapability{},
		FrontendCompatibility: appauthority.FrontendCompatibility{Mode: appauthority.FrontendModeNone}}
	manifest.MetadataBundleDigest, err = appauthority.MetadataBundleDigest(bundle, manifest)
	if err != nil {
		t.Fatal(err)
	}
	pkg, err := appauthority.VerifyPackage(appauthority.Package{Manifest: manifest, Bundle: bundle, ComponentBlobs: blobs})
	if err != nil {
		t.Fatal(err)
	}
	return testSignedImportPackage(t, pkg, issuedAt)
}

func testSignedImportPackage(t *testing.T, pkg appauthority.VerifiedPackage, issuedAt time.Time) ([]byte, []byte) {
	t.Helper()
	_, packageRaw, err := releaseartifact.BuildPackage(pkg.Package)
	if err != nil {
		t.Fatal(err)
	}
	privateKey := testImportPrivateKey()
	_, attestationRaw, err := releaseartifact.SignAttestation(pkg, releaseartifact.Provenance{
		SchemaVersion: releaseartifact.ProvenanceSchemaVersion, SourceRepository: "example/crm", SourceRevision: strings.Repeat("a", 40),
		SourcePath: "metadata", SourceTreeDigest: "sha256:" + fmt.Sprintf("%x", sha256.Sum256(packageRaw)),
		BuilderID: "example/builder/v1", ContractModuleVersion: "v0.1.8",
	}, "release-key-1", issuedAt, privateKey)
	if err != nil {
		t.Fatal(err)
	}
	return packageRaw, attestationRaw
}

func testImportPrivateKey() ed25519.PrivateKey {
	seed := sha256.Sum256([]byte("catalogapp-artifact-import-test-key"))
	return ed25519.NewKeyFromSeed(seed[:])
}

func testImportPublicKey() ed25519.PublicKey {
	return testImportPrivateKey().Public().(ed25519.PublicKey)
}

func alternateImportPublicKey() ed25519.PublicKey {
	key := append(ed25519.PublicKey(nil), testImportPublicKey()...)
	key[0] ^= 0xff
	return key
}

func testReleaseSigningKey() PublisherKey {
	return PublisherKey{
		KeyID:      "key-1",
		Algorithm:  releaseartifact.SignatureAlgorithm,
		PublicKey:  testImportPublicKey(),
		ValidFrom:  time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC),
		ValidUntil: time.Date(2100, time.January, 1, 0, 0, 0, 0, time.UTC),
	}
}

func testVerifiedReleaseRegistration(t *testing.T, release RegisteredRelease, verifiedAt time.Time) verifiedReleaseRegistration {
	t.Helper()
	verifiedAt = verifiedAt.UTC().Truncate(time.Microsecond)
	if verifiedAt.IsZero() {
		t.Fatal("test verification time must be present")
	}
	key := testReleaseSigningKey()
	_, packageRaw, err := releaseartifact.BuildPackage(release.Package.Package)
	if err != nil {
		t.Fatal(err)
	}
	packageHash := sha256.Sum256(packageRaw)
	_, canonicalAttestation, err := releaseartifact.SignAttestation(release.Package, releaseartifact.Provenance{
		SchemaVersion:         releaseartifact.ProvenanceSchemaVersion,
		SourceRepository:      "example/catalogapp-test",
		SourceRevision:        strings.Repeat("a", 40),
		SourcePath:            "metadata",
		SourceTreeDigest:      "sha256:" + fmt.Sprintf("%x", packageHash),
		BuilderID:             "catalogapp-test/v1",
		ContractModuleVersion: "v0.0.0-test",
	}, key.KeyID, verifiedAt.Add(-time.Microsecond), testImportPrivateKey())
	if err != nil {
		t.Fatal(err)
	}
	artifact, err := releaseartifact.Verify(packageRaw, canonicalAttestation, ed25519.PublicKey(key.PublicKey))
	if err != nil {
		t.Fatal(err)
	}
	return verifiedReleaseRegistration{
		artifact:             artifact,
		canonicalAttestation: canonicalAttestation,
		verifiedAt:           verifiedAt,
		signingKey: releaseSigningKey{
			algorithm: key.Algorithm, publicKey: append([]byte(nil), key.PublicKey...),
			validFrom: key.ValidFrom, validUntil: key.ValidUntil,
		},
	}
}
