package packs

import (
	"context"
	"database/sql"
	"database/sql/driver"
	"encoding/json"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/nexuscrm/files"
	"github.com/nexuscrm/kernel/pkg/formula"
	"github.com/nexuscrm/metadata"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/fieldprovenance"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/nexuscrm/shared/pkg/searchmetadata"
	cp "github.com/pingcap-inc/nexuscrm-contract/pkg/controlplane"
	"github.com/pingcap-inc/nexuscrm-controlplane/packspec"
)

func expectInstalledPackRuntimeDeclarationColumns(mock sqlmock.Sqlmock, present bool) {
	expectInstalledPackRuntimeDeclarationColumnsWithStartupShallow(mock, present, present)
}

func expectInstalledPackRuntimeDeclarationColumnsWithStartupShallow(mock sqlmock.Sqlmock, present bool, startupShallowPresent bool) {
	expectInstalledPackRuntimeDeclarationColumnsWithStartupShallowAndFormulaBundles(mock, present, startupShallowPresent, false)
}

func expectInstalledPackRuntimeDeclarationColumnsWithStartupShallowAndFormulaBundles(mock sqlmock.Sqlmock, present bool, startupShallowPresent bool, formulaBundlesPresent bool) {
	rows := sqlmock.NewRows([]string{"COLUMN_NAME"})
	if present {
		rows.
			AddRow("RuntimeObjectSpecDeclarations").
			AddRow("RuntimeRelatedListRules").
			AddRow("RuntimeSearchScopeOrder").
			AddRow("RuntimeSearchAllDefaultFields")
	}
	mock.ExpectQuery("SELECT COLUMN_NAME\\s+FROM INFORMATION_SCHEMA\\.COLUMNS").
		WithArgs("_System_InstalledPack", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields").
		WillReturnRows(rows)
	if present {
		mock.ExpectQuery("SELECT COUNT\\(\\*\\)\\s+FROM INFORMATION_SCHEMA\\.COLUMNS").
			WithArgs("_System_InstalledPack", "RuntimeStartupShallowDeclarations").
			WillReturnRows(sqlmock.NewRows([]string{"COUNT(*)"}).AddRow(boolToInt(startupShallowPresent)))
		mock.ExpectQuery("SELECT COUNT\\(\\*\\)\\s+FROM INFORMATION_SCHEMA\\.COLUMNS").
			WithArgs("_System_InstalledPack", "RuntimeFormulaFunctionBundles").
			WillReturnRows(sqlmock.NewRows([]string{"COUNT(*)"}).AddRow(boolToInt(formulaBundlesPresent)))
	}
}

func TestResolveActiveInstalledRuntimePackRejectsCurrentSourceHashDrift(t *testing.T) {
	sourceDir := writeRuntimeTestPackSource(t, "jira-like-sample")
	initGitTestRepo(t, sourceDir)
	commitOne := gitTestCommit(t, sourceDir, "initial")

	packAtCommitOne, err := ResolveInstalledSourceBackedPackForInstalledRowAtSourceRevision(
		"jira-like-sample",
		AppPack,
		SupportedManifestFormatVersion,
		PackSourceKindLocalPath,
		sourceDir,
		commitOne,
	)
	if err != nil {
		t.Fatalf("ResolveInstalledSourceBackedPackForInstalledRowAtSourceRevision(commitOne): %v", err)
	}
	hashOne, err := PackPayloadHash(packAtCommitOne)
	if err != nil {
		t.Fatalf("PackPayloadHash(commitOne): %v", err)
	}

	mutateRuntimeTestPackSource(t, sourceDir)
	_ = gitTestCommit(t, sourceDir, "mutated")

	pack, err := resolveActiveInstalledRuntimePack(context.Background(), nil, activeInstalledPackRow{
		name:                  "jira-like-sample",
		category:              string(AppPack),
		appliedPayloadHash:    sql.NullString{Valid: true, String: hashOne},
		manifestFormatVersion: sql.NullString{Valid: true, String: SupportedManifestFormatVersion},
		sourceKind:            sql.NullString{Valid: true, String: string(PackSourceKindLocalPath)},
		sourceURI:             sql.NullString{Valid: true, String: sourceDir},
		sourceRevision:        sql.NullString{Valid: true, String: commitOne},
	}, nil, false, nil)
	if err == nil {
		t.Fatalf("resolveActiveInstalledRuntimePack returned %T nil error, want payload hash drift", pack)
	}
	if !strings.Contains(err.Error(), "payload hash") {
		t.Fatalf("resolveActiveInstalledRuntimePack error = %v, want payload hash drift", err)
	}
}

func TestResolveActiveInstalledRuntimePackUsesStoredOfficialSourceRevision(t *testing.T) {
	const packName = "active-runtime-revision"

	officialRoot := t.TempDir()
	packRoot := filepath.Join(officialRoot, packName)
	manifest := strings.Replace(validPackManifest(`[
    {
      "kind": "object_spec",
      "businessKey": "issue",
      "manageability": "locked",
      "path": "objects/issue.json"
    }
  ]`, `"dependencies": [],`), `"name": "jira-like-sample"`, `"name": "`+packName+`"`, 1)
	writeManifest(t, packRoot, manifest)
	writeFile(t, packRoot, "objects/issue.json", testObjectSpecPayloadWithFields("Issue",
		`{"apiName": "Subject", "label": "Subject", "type": "string", "required": true, "isNameField": true, "description": "installed revision"}`,
	))
	initGitTestRepo(t, officialRoot)
	installedRevision := gitTestCommit(t, officialRoot, "installed revision")

	opts := DefaultPackSourceOptions()
	opts.OfficialRepoRoot = officialRoot
	installedPack, err := ResolveInstalledSourceBackedPackForInstalledRowAtSourceRevisionWithPackSourceOptions(
		packName, AppPack, SupportedManifestFormatVersion,
		PackSourceKindOfficialRepo, packName, installedRevision, opts,
	)
	if err != nil {
		t.Fatalf("resolve installed revision: %v", err)
	}
	installedHash, err := PackPayloadHash(installedPack)
	if err != nil {
		t.Fatalf("installed revision payload hash: %v", err)
	}

	writeFile(t, packRoot, "objects/issue.json", testObjectSpecPayloadWithFields("Issue",
		`{"apiName": "Subject", "label": "Subject", "type": "string", "required": true, "isNameField": true, "description": "checkout head"}`,
	))
	checkoutHead := gitTestCommit(t, officialRoot, "checkout head")
	if checkoutHead == installedRevision {
		t.Fatal("test fixture produced identical git revisions")
	}
	t.Setenv(OfficialAppPacksRootEnvVar, officialRoot)

	pack, err := resolveActiveInstalledRuntimePack(context.Background(), nil, activeInstalledPackRow{
		name:                  packName,
		category:              string(AppPack),
		appliedPayloadHash:    sql.NullString{Valid: true, String: installedHash},
		manifestFormatVersion: sql.NullString{Valid: true, String: SupportedManifestFormatVersion},
		sourceKind:            sql.NullString{Valid: true, String: string(PackSourceKindOfficialRepo)},
		sourceURI:             sql.NullString{Valid: true, String: packName},
		sourceRevision:        sql.NullString{Valid: true, String: installedRevision},
	}, nil, false, nil)
	if err != nil {
		t.Fatalf("resolve active installed runtime pack: %v", err)
	}
	meta := PackHashMetadataForPack(pack)
	if meta.SourceRevision != installedRevision {
		t.Fatalf("resolved source revision = %q, want installed revision %q", meta.SourceRevision, installedRevision)
	}
	if meta.SourceRevision == checkoutHead {
		t.Fatalf("resolved source revision = checkout HEAD %q, want installed revision", checkoutHead)
	}
}

func TestResolveActiveInstalledRuntimePacksUsesInstalledSourceIdentity(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	rawSourceDir := writeRuntimeTestPackSource(t, "jira-like-sample")
	sourceDir, err := filepath.EvalSymlinks(rawSourceDir)
	if err != nil {
		t.Fatalf("EvalSymlinks: %v", err)
	}
	pack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		sourceDir,
	)
	if err != nil {
		t.Fatalf("resolve source-backed pack: %v", err)
	}
	payloadHash, err := PackPayloadHash(pack)
	if err != nil {
		t.Fatalf("payload hash: %v", err)
	}

	expectInstalledPackRuntimeDeclarationColumns(mock, false)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision\\s+FROM _System_InstalledPack").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
		}).AddRow(
			"jira-like-sample",
			string(AppPack),
			payloadHash,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			sourceDir,
			nil,
		))

	packs, err := ResolveActiveInstalledRuntimePacks(context.Background(), db)
	if err != nil {
		t.Fatalf("ResolveActiveInstalledRuntimePacks: %v", err)
	}
	if len(packs) != 1 {
		t.Fatalf("resolved pack count = %d, want 1", len(packs))
	}
	sourceBacked, ok := packs[0].(*SourceBackedPack)
	if !ok {
		t.Fatalf("resolved pack type = %T, want *SourceBackedPack", packs[0])
	}
	meta := PackHashMetadataForPack(sourceBacked)
	if !meta.SourceKindValid || meta.SourceKind != string(PackSourceKindLocalPath) {
		t.Fatalf("resolved SourceKind = %#v, want local_path", meta)
	}
	if !meta.SourceURIValid || meta.SourceURI != sourceDir {
		t.Fatalf("resolved SourceURI = %#v, want %q", meta, sourceDir)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestActiveInstalledPackNamesReadsOnlyActiveNonDeletedRows(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	expectInstalledPackRuntimeDeclarationColumns(mock, false)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision\\s+FROM _System_InstalledPack\\s+WHERE Status = 'active' AND COALESCE\\(IsDeleted, 0\\) = 0").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
		}).
			AddRow(" salesforce-crm-standard ", string(AppPack), nil, nil, nil, nil, nil).
			AddRow("  ", string(AppPack), nil, nil, nil, nil, nil).
			AddRow("jira-like-sample", string(AppPack), nil, nil, nil, nil, nil))

	names, err := ActiveInstalledPackNames(context.Background(), db)
	if err != nil {
		t.Fatalf("ActiveInstalledPackNames: %v", err)
	}
	want := map[string]bool{
		"salesforce-crm-standard": true,
		"jira-like-sample":        true,
	}
	if !reflect.DeepEqual(names, want) {
		t.Fatalf("active names = %#v, want %#v", names, want)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestActiveInstalledAppPackNamesReadsOnlyActiveAppPacks(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	mock.ExpectQuery("SELECT Name, SourceKind, SourceURI, SourceRevision, AppliedPayloadHash, DesiredPayloadHash\\s+FROM _System_InstalledPack\\s+WHERE Status = 'active' AND Category = \\? AND COALESCE\\(IsDeleted, 0\\) = 0").
		WithArgs(string(AppPack)).
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "SourceKind", "SourceURI", "SourceRevision", "AppliedPayloadHash", "DesiredPayloadHash",
		}).
			AddRow(" salesforce-crm-standard ", PackSourceKindOfficialRepo, "salesforce-crm-standard", "rev-crm", "hash-crm", "hash-crm").
			AddRow("jira-like-sample", PackSourceKindLocalPath, "/tmp/jira", "rev-jira", "hash-jira", "hash-jira").
			AddRow("  ", PackSourceKindOfficialRepo, "blank", "rev-blank", "hash-blank", "hash-blank"))

	names, err := ActiveInstalledAppPackNames(context.Background(), db)
	if err != nil {
		t.Fatalf("ActiveInstalledAppPackNames: %v", err)
	}
	want := []string{"jira-like-sample", "salesforce-crm-standard"}
	if !reflect.DeepEqual(names, want) {
		t.Fatalf("active app pack names = %#v, want %#v", names, want)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestActiveInstalledAppPacksProjectsSourceAndHashIdentity(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	mock.ExpectQuery("SELECT Name, SourceKind, SourceURI, SourceRevision, AppliedPayloadHash, DesiredPayloadHash\\s+FROM _System_InstalledPack\\s+WHERE Status = 'active' AND Category = \\? AND COALESCE\\(IsDeleted, 0\\) = 0").
		WithArgs(string(AppPack)).
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "SourceKind", "SourceURI", "SourceRevision", "AppliedPayloadHash", "DesiredPayloadHash",
		}).AddRow(" salesforce-crm-standard ", " official_repo ", " salesforce-crm-standard ", " rev ", " applied ", " desired "))

	rows, err := ActiveInstalledAppPacks(context.Background(), db)
	if err != nil {
		t.Fatalf("ActiveInstalledAppPacks: %v", err)
	}
	if len(rows) != 1 {
		t.Fatalf("rows len = %d, want 1", len(rows))
	}
	want := ActiveInstalledAppPack{
		Name:               "salesforce-crm-standard",
		SourceKind:         "official_repo",
		SourceURI:          "salesforce-crm-standard",
		SourceRevision:     "rev",
		AppliedPayloadHash: "applied",
		DesiredPayloadHash: "desired",
	}
	if !reflect.DeepEqual(rows[0], want) {
		t.Fatalf("row = %#v, want %#v", rows[0], want)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestKnownInstalledPackCatalogReadsAllNonDeletedRowsAndUnionsPlatform(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	// NO Status filter: the catalog must read every non-deleted row across ALL
	// statuses (active + installing/removing/failed). The query is the new
	// base-column read, distinct from activeInstalledPackRows' WHERE Status='active'.
	mock.ExpectQuery("SELECT Name, Status\\s+FROM _System_InstalledPack\\s+WHERE COALESCE\\(IsDeleted, 0\\) = 0\\s+ORDER BY Name").
		WillReturnRows(sqlmock.NewRows([]string{"Name", "Status"}).
			AddRow(" salesforce-crm-standard ", "active").
			AddRow("jira-installing", "installing").
			AddRow("jira-removing", "removing").
			AddRow("jira-failed", "failed").
			AddRow("  ", "active"))

	catalog, err := KnownInstalledPackCatalog(context.Background(), db)
	if err != nil {
		t.Fatalf("KnownInstalledPackCatalog: %v", err)
	}

	// active -> true; installing/removing/failed each -> false (known-but-inactive).
	if got, ok := catalog["salesforce-crm-standard"]; !ok || got != true {
		t.Fatalf("salesforce-crm-standard = (%v, present=%v), want (true, true)", got, ok)
	}
	for _, inactive := range []string{"jira-installing", "jira-removing", "jira-failed"} {
		got, ok := catalog[inactive]
		if !ok {
			t.Fatalf("inactive pack %q absent from catalog, want present with false", inactive)
		}
		if got != false {
			t.Fatalf("inactive pack %q = true, want false (known-but-inactive)", inactive)
		}
	}
	// Blank-name row is dropped.
	if _, ok := catalog["  "]; ok {
		t.Fatal("blank-name row must not appear in catalog")
	}
	if _, ok := catalog[""]; ok {
		t.Fatal("empty-name key must not appear in catalog")
	}
	// Every platform pack is unioned in as known & active.
	for _, p := range allKnownPacks() {
		got, ok := catalog[p.Name()]
		if !ok {
			t.Fatalf("platform pack %q absent from catalog, want present with true", p.Name())
		}
		if got != true {
			t.Fatalf("platform pack %q = false, want true", p.Name())
		}
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestKnownInstalledPackCatalogKeepsOnlyQueryReturnedRows(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	// The WHERE COALESCE(IsDeleted,0)=0 predicate lives in the SQL, so the driver
	// returns only non-deleted rows; the catalog relies on that predicate rather
	// than re-filtering IsDeleted in Go. Assert a returned row lands as expected.
	mock.ExpectQuery("SELECT Name, Status\\s+FROM _System_InstalledPack\\s+WHERE COALESCE\\(IsDeleted, 0\\) = 0\\s+ORDER BY Name").
		WillReturnRows(sqlmock.NewRows([]string{"Name", "Status"}).
			AddRow("kept-pack", "active"))

	catalog, err := KnownInstalledPackCatalog(context.Background(), db)
	if err != nil {
		t.Fatalf("KnownInstalledPackCatalog: %v", err)
	}
	if got, ok := catalog["kept-pack"]; !ok || got != true {
		t.Fatalf("kept-pack = (%v, present=%v), want (true, true)", got, ok)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

// TestKnownCatalogActiveSubsetEqualsActiveInstalledPackNames pins acceptance #6:
// the active-only ActiveInstalledPackNames projection is exactly the value==true
// subset of the 3-state KnownInstalledPackCatalog, after dropping the platform-pack
// floor (which the active-only projection never reads).
func TestKnownCatalogActiveSubsetEqualsActiveInstalledPackNames(t *testing.T) {
	platformNames := map[string]bool{}
	for _, p := range allKnownPacks() {
		platformNames[p.Name()] = true
	}

	// ActiveInstalledPackNames reads through activeInstalledPackRows: WHERE
	// Status='active'. Feed it only the active rows of the fixed row set.
	dbActive, mockActive, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock active: %v", err)
	}
	defer dbActive.Close()
	expectInstalledPackRuntimeDeclarationColumns(mockActive, false)
	mockActive.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision\\s+FROM _System_InstalledPack\\s+WHERE Status = 'active' AND COALESCE\\(IsDeleted, 0\\) = 0").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
		}).AddRow("jira-active", string(AppPack), nil, nil, nil, nil, nil))
	activeNames, err := ActiveInstalledPackNames(context.Background(), dbActive)
	if err != nil {
		t.Fatalf("ActiveInstalledPackNames: %v", err)
	}

	// KnownInstalledPackCatalog reads ALL non-deleted rows of the SAME fixed row set.
	dbKnown, mockKnown, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock known: %v", err)
	}
	defer dbKnown.Close()
	mockKnown.ExpectQuery("SELECT Name, Status\\s+FROM _System_InstalledPack\\s+WHERE COALESCE\\(IsDeleted, 0\\) = 0\\s+ORDER BY Name").
		WillReturnRows(sqlmock.NewRows([]string{"Name", "Status"}).
			AddRow("jira-active", "active").
			AddRow("jira-installing", "installing"))
	catalog, err := KnownInstalledPackCatalog(context.Background(), dbKnown)
	if err != nil {
		t.Fatalf("KnownInstalledPackCatalog: %v", err)
	}

	// The true-valued subset of the catalog, minus the platform floor, must equal
	// the active-only projection exactly.
	gotActiveSubset := map[string]bool{}
	for name, active := range catalog {
		if active && !platformNames[name] {
			gotActiveSubset[name] = true
		}
	}
	if !reflect.DeepEqual(gotActiveSubset, activeNames) {
		t.Fatalf("active subset of catalog = %#v, want ActiveInstalledPackNames %#v", gotActiveSubset, activeNames)
	}
	if err := mockActive.ExpectationsWereMet(); err != nil {
		t.Fatalf("active sql expectations: %v", err)
	}
	if err := mockKnown.ExpectationsWereMet(); err != nil {
		t.Fatalf("known sql expectations: %v", err)
	}
}

func TestResolveActiveInstalledRuntimePacksRejectsRowsWithoutSourceIdentity(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	expectInstalledPackRuntimeDeclarationColumns(mock, false)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision\\s+FROM _System_InstalledPack").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
		}).AddRow(
			"salesforce-crm-standard",
			string(AppPack),
			"nexus-pack-sha256-v1:0000000000000000000000000000000000000000000000000000000000000000",
			nil,
			nil,
			nil,
			nil,
		))

	_, err = ResolveActiveInstalledRuntimePacks(context.Background(), db)
	if err == nil {
		t.Fatal("ResolveActiveInstalledRuntimePacks = nil, want missing source identity error")
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledRuntimeDeclarationsRejectsPersistedDeclarationsWithoutSourceIdentity(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	sourceDir := writeRuntimeTestPackSource(t, "jira-like-sample")
	pack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		sourceDir,
	)
	if err != nil {
		t.Fatalf("resolve source-backed pack: %v", err)
	}
	decl, err := BuildPersistedRuntimeDeclaration(pack)
	if err != nil {
		t.Fatalf("build persisted runtime declaration: %v", err)
	}
	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"jira-like-sample",
			string(AppPack),
			nil,
			nil,
			nil,
			nil,
			nil,
			decl.ObjectPayloadsJSON(),
			decl.RelatedListRulesJSON(),
			decl.SearchScopeOrderJSON(),
			decl.SearchAllDefaultFieldsJSON(),
			decl.StartupShallowDeclaredRowsJSON(),
		))

	_, err = LoadActiveInstalledRuntimeDeclarations(context.Background(), db)
	if err == nil || !strings.Contains(err.Error(), "persisted runtime declaration is not source-backed active state") {
		t.Fatalf("LoadActiveInstalledRuntimeDeclarations error = %v, want source-backed active state rejection", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledRuntimeDeclarationsRejectsDriftedPersistedDeclarationsWhenSourceIdentityPresent(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	rawSourceDir := writeRuntimeTestPackSource(t, "jira-like-sample")
	sourceDir, err := filepath.EvalSymlinks(rawSourceDir)
	if err != nil {
		t.Fatalf("EvalSymlinks: %v", err)
	}
	pack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		sourceDir,
	)
	if err != nil {
		t.Fatalf("resolve source-backed pack: %v", err)
	}
	decl, err := BuildPersistedRuntimeDeclaration(pack)
	if err != nil {
		t.Fatalf("build persisted runtime declaration: %v", err)
	}
	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"jira-like-sample",
			string(AppPack),
			payloadHashOrFatal(t, pack),
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			sourceDir,
			nil,
			`[]`,
			decl.RelatedListRulesJSON(),
			decl.SearchScopeOrderJSON(),
			decl.SearchAllDefaultFieldsJSON(),
			decl.StartupShallowDeclaredRowsJSON(),
		))

	_, err = LoadActiveInstalledRuntimeDeclarations(context.Background(), db)
	if err == nil || !strings.Contains(err.Error(), "persisted runtime declaration does not match source identity") {
		t.Fatalf("LoadActiveInstalledRuntimeDeclarations error = %v, want source-vs-persisted drift error", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledRuntimeDeclarationsRejectsDriftedStartupShallowWhenSourceIdentityPresent(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	rawSourceDir := writeRuntimeTestPackSource(t, "jira-like-sample")
	sourceDir, err := filepath.EvalSymlinks(rawSourceDir)
	if err != nil {
		t.Fatalf("EvalSymlinks: %v", err)
	}
	pack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		sourceDir,
	)
	if err != nil {
		t.Fatalf("resolve source-backed pack: %v", err)
	}
	decl, err := BuildPersistedRuntimeDeclaration(pack)
	if err != nil {
		t.Fatalf("build persisted runtime declaration: %v", err)
	}
	activeProjection, err := marshalRuntimeStartupShallowProjection(runtimeStartupShallowValidationRuleProjection{
		ObjectAPIName: "Issue",
		Name:          "UnexpectedActive",
		Active:        true,
		Condition:     "ISBLANK(Subject)",
		ErrorMessage:  "Subject is required",
		OwnerPack:     "jira-like-sample",
	})
	if err != nil {
		t.Fatalf("marshalRuntimeStartupShallowProjection: %v", err)
	}
	startupShallowJSON, err := marshalRuntimeStartupShallowDeclaredRows([]runtimeStartupShallowDeclaredRow{{
		Kind:        ComponentKindValidationRule,
		BusinessKey: JoinComponentBusinessKey("Issue", "UnexpectedActive"),
		Projection:  activeProjection,
	}})
	if err != nil {
		t.Fatalf("marshalRuntimeStartupShallowDeclaredRows: %v", err)
	}
	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"jira-like-sample",
			string(AppPack),
			payloadHashOrFatal(t, pack),
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			sourceDir,
			nil,
			decl.ObjectPayloadsJSON(),
			decl.RelatedListRulesJSON(),
			decl.SearchScopeOrderJSON(),
			decl.SearchAllDefaultFieldsJSON(),
			startupShallowJSON,
		))

	_, err = LoadActiveInstalledRuntimeDeclarations(context.Background(), db)
	if err == nil || !strings.Contains(err.Error(), "persisted runtime declaration does not match source identity") {
		t.Fatalf("LoadActiveInstalledRuntimeDeclarations error = %v, want source-vs-persisted drift error", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledRuntimeDeclarationsRejectsMissingStartupShallowWhenSourceDeclaresRows(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	rawSourceDir := writeRuntimeValidationRulePackSource(t, "jira-like-sample")
	sourceDir, err := filepath.EvalSymlinks(rawSourceDir)
	if err != nil {
		t.Fatalf("EvalSymlinks: %v", err)
	}
	pack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		sourceDir,
	)
	if err != nil {
		t.Fatalf("resolve source-backed pack: %v", err)
	}
	decl, err := BuildPersistedRuntimeDeclaration(pack)
	if err != nil {
		t.Fatalf("build persisted runtime declaration: %v", err)
	}
	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"jira-like-sample",
			string(AppPack),
			payloadHashOrFatal(t, pack),
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			sourceDir,
			nil,
			decl.ObjectPayloadsJSON(),
			decl.RelatedListRulesJSON(),
			decl.SearchScopeOrderJSON(),
			decl.SearchAllDefaultFieldsJSON(),
			nil,
		))

	_, err = LoadActiveInstalledRuntimeDeclarations(context.Background(), db)
	if err == nil || !strings.Contains(err.Error(), "persisted runtime declaration does not match source identity") {
		t.Fatalf("LoadActiveInstalledRuntimeDeclarations error = %v, want source-vs-persisted drift error", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledRuntimeDeclarationsAcceptsLegacyObjectSpecListFieldsStartupShallowOmission(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	rawSourceDir := writeRuntimeListFieldsPackSource(t, "jira-like-sample")
	sourceDir, err := filepath.EvalSymlinks(rawSourceDir)
	if err != nil {
		t.Fatalf("EvalSymlinks: %v", err)
	}
	pack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		sourceDir,
	)
	if err != nil {
		t.Fatalf("resolve source-backed pack: %v", err)
	}
	decl, err := BuildPersistedRuntimeDeclaration(pack)
	if err != nil {
		t.Fatalf("build persisted runtime declaration: %v", err)
	}
	legacyStartupShallowJSON, err := marshalRuntimeStartupShallowDeclaredRows(nil)
	if err != nil {
		t.Fatalf("marshalRuntimeStartupShallowDeclaredRows: %v", err)
	}
	if legacyStartupShallowJSON == decl.StartupShallowDeclaredRowsJSON() {
		t.Fatal("test setup expected legacy startup-shallow declaration to omit object_spec-derived list_fields")
	}

	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"jira-like-sample",
			string(AppPack),
			payloadHashOrFatal(t, pack),
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			sourceDir,
			nil,
			decl.ObjectPayloadsJSON(),
			decl.RelatedListRulesJSON(),
			decl.SearchScopeOrderJSON(),
			decl.SearchAllDefaultFieldsJSON(),
			legacyStartupShallowJSON,
		))

	packList, err := LoadActiveInstalledRuntimeDeclarations(context.Background(), db)
	if err != nil {
		t.Fatalf("LoadActiveInstalledRuntimeDeclarations: %v", err)
	}
	if len(packList) != 1 || packList[0].Name() != "jira-like-sample" {
		t.Fatalf("loaded packs = %#v, want jira-like-sample", packList)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledRuntimeDeclarationsAcceptsMissingStartupShallowColumnWhenSourceDeclaresRows(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	rawSourceDir := writeRuntimeValidationRulePackSource(t, "jira-like-sample")
	sourceDir, err := filepath.EvalSymlinks(rawSourceDir)
	if err != nil {
		t.Fatalf("EvalSymlinks: %v", err)
	}
	pack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		sourceDir,
	)
	if err != nil {
		t.Fatalf("resolve source-backed pack: %v", err)
	}
	decl, err := BuildPersistedRuntimeDeclaration(pack)
	if err != nil {
		t.Fatalf("build persisted runtime declaration: %v", err)
	}
	if decl.StartupShallowDeclaredRowsJSON() == "[]" {
		t.Fatal("test setup expected source pack to declare startup-shallow rows")
	}
	expectInstalledPackRuntimeDeclarationColumnsWithStartupShallow(mock, true, false)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields",
		}).AddRow(
			"jira-like-sample",
			string(AppPack),
			payloadHashOrFatal(t, pack),
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			sourceDir,
			nil,
			decl.ObjectPayloadsJSON(),
			decl.RelatedListRulesJSON(),
			decl.SearchScopeOrderJSON(),
			decl.SearchAllDefaultFieldsJSON(),
		))

	packList, err := LoadActiveInstalledRuntimeDeclarations(context.Background(), db)
	if err != nil {
		t.Fatalf("LoadActiveInstalledRuntimeDeclarations: %v", err)
	}
	if len(packList) != 1 {
		t.Fatalf("loaded pack count = %d, want 1", len(packList))
	}
	if _, ok := packList[0].(*persistedRuntimePack); !ok {
		t.Fatalf("loaded pack type = %T, want *persistedRuntimePack", packList[0])
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledRuntimeDeclarationsRejectsRuntimeDriftWhenStartupShallowColumnMissing(t *testing.T) {
	rawSourceDir := writeRuntimeValidationRulePackSource(t, "jira-like-sample")
	sourceDir, err := filepath.EvalSymlinks(rawSourceDir)
	if err != nil {
		t.Fatalf("EvalSymlinks: %v", err)
	}
	pack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		sourceDir,
	)
	if err != nil {
		t.Fatalf("resolve source-backed pack: %v", err)
	}
	decl, err := BuildPersistedRuntimeDeclaration(pack)
	if err != nil {
		t.Fatalf("build persisted runtime declaration: %v", err)
	}

	cases := []struct {
		name          string
		objectJSON    string
		relatedJSON   string
		searchJSON    string
		searchAllJSON string
	}{
		{
			name:          "object",
			objectJSON:    `[]`,
			relatedJSON:   decl.RelatedListRulesJSON(),
			searchJSON:    decl.SearchScopeOrderJSON(),
			searchAllJSON: decl.SearchAllDefaultFieldsJSON(),
		},
		{
			name:          "related-list",
			objectJSON:    decl.ObjectPayloadsJSON(),
			relatedJSON:   `[]`,
			searchJSON:    decl.SearchScopeOrderJSON(),
			searchAllJSON: decl.SearchAllDefaultFieldsJSON(),
		},
		{
			name:          "search-scope",
			objectJSON:    decl.ObjectPayloadsJSON(),
			relatedJSON:   decl.RelatedListRulesJSON(),
			searchJSON:    `[]`,
			searchAllJSON: decl.SearchAllDefaultFieldsJSON(),
		},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			db, mock, err := sqlmock.New()
			if err != nil {
				t.Fatalf("sqlmock: %v", err)
			}
			defer db.Close()

			expectInstalledPackRuntimeDeclarationColumnsWithStartupShallow(mock, true, false)
			mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
				WillReturnRows(sqlmock.NewRows([]string{
					"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields",
				}).AddRow(
					"jira-like-sample",
					string(AppPack),
					payloadHashOrFatal(t, pack),
					SupportedManifestFormatVersion,
					string(PackSourceKindLocalPath),
					sourceDir,
					nil,
					tc.objectJSON,
					tc.relatedJSON,
					tc.searchJSON,
					tc.searchAllJSON,
				))

			_, err = LoadActiveInstalledRuntimeDeclarations(context.Background(), db)
			if err == nil || !strings.Contains(err.Error(), "persisted runtime declaration does not match source identity") {
				t.Fatalf("LoadActiveInstalledRuntimeDeclarations error = %v, want source-vs-persisted drift error", err)
			}
			if err := mock.ExpectationsWereMet(); err != nil {
				t.Fatalf("sql expectations: %v", err)
			}
		})
	}
}

func TestLoadActiveInstalledRuntimeDeclarationsUsesSourceBackedFallbackWhenPersistedDeclarationMissing(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	rawSourceDir := writeRuntimeTestPackSource(t, "jira-like-sample")
	sourceDir, err := filepath.EvalSymlinks(rawSourceDir)
	if err != nil {
		t.Fatalf("EvalSymlinks: %v", err)
	}
	pack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		sourceDir,
	)
	if err != nil {
		t.Fatalf("resolve source-backed pack: %v", err)
	}
	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"jira-like-sample",
			string(AppPack),
			payloadHashOrFatal(t, pack),
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			sourceDir,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
		))

	packList, err := LoadActiveInstalledRuntimeDeclarations(context.Background(), db)
	if err != nil {
		t.Fatalf("LoadActiveInstalledRuntimeDeclarations: %v", err)
	}
	if len(packList) != 1 {
		t.Fatalf("loaded pack count = %d, want 1", len(packList))
	}
	if _, ok := packList[0].(*SourceBackedPack); !ok {
		t.Fatalf("loaded pack type = %T, want *SourceBackedPack", packList[0])
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledRuntimeDeclarationsRejectsPersistedDeclarationsWithoutAppliedHash(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	rawSourceDir := writeRuntimeTestPackSource(t, "jira-like-sample")
	sourceDir, err := filepath.EvalSymlinks(rawSourceDir)
	if err != nil {
		t.Fatalf("EvalSymlinks: %v", err)
	}
	pack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		sourceDir,
	)
	if err != nil {
		t.Fatalf("resolve source-backed pack: %v", err)
	}
	decl, err := BuildPersistedRuntimeDeclaration(pack)
	if err != nil {
		t.Fatalf("build persisted runtime declaration: %v", err)
	}
	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"jira-like-sample",
			string(AppPack),
			nil,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			sourceDir,
			nil,
			decl.ObjectPayloadsJSON(),
			decl.RelatedListRulesJSON(),
			decl.SearchScopeOrderJSON(),
			decl.SearchAllDefaultFieldsJSON(),
			decl.StartupShallowDeclaredRowsJSON(),
		))

	_, err = LoadActiveInstalledRuntimeDeclarations(context.Background(), db)
	if err == nil || !strings.Contains(err.Error(), "persisted runtime declaration is not source-backed active state") {
		t.Fatalf("LoadActiveInstalledRuntimeDeclarations error = %v, want source-backed active state rejection", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledRuntimeDeclarationsRejectsMissingPersistedDeclaration(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"jira-like-sample",
			string(AppPack),
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
		))

	_, err = LoadActiveInstalledRuntimeDeclarations(context.Background(), db)
	if err == nil || !strings.Contains(err.Error(), "missing persisted runtime declaration") {
		t.Fatalf("LoadActiveInstalledRuntimeDeclarations error = %v, want missing declaration error", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledRuntimeDeclarationsSurfacesSourceResolveError(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	missingSourceDir := filepath.Join(t.TempDir(), "missing-pack")
	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"jira-like-sample",
			string(AppPack),
			"stale-hash",
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			missingSourceDir,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
		))

	_, err = LoadActiveInstalledRuntimeDeclarations(context.Background(), db)
	if err == nil {
		t.Fatal("LoadActiveInstalledRuntimeDeclarations error = nil, want source resolve error")
	}
	if strings.Contains(err.Error(), "missing persisted runtime declaration") {
		t.Fatalf("LoadActiveInstalledRuntimeDeclarations swallowed source resolve error: %v", err)
	}
	if !strings.Contains(err.Error(), "resolve installed source-backed pack jira-like-sample") {
		t.Fatalf("LoadActiveInstalledRuntimeDeclarations error = %v, want source-backed resolve diagnostic", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func payloadHashOrFatal(t *testing.T, pack Pack) string {
	t.Helper()
	hash, err := PackPayloadHash(pack)
	if err != nil {
		t.Fatalf("PackPayloadHash: %v", err)
	}
	return hash
}

func writeRuntimeConnectorAssociationPackSource(t *testing.T) string {
	t.Helper()

	root := writePackFixture(t, "runtime-connector-association-active", validPackManifest(`[
    {
      "kind": "object_spec",
      "businessKey": "crm",
      "manageability": "locked",
      "path": "objects/crm.json"
    },
    {"kind":"standard_flow","businessKey":"Opportunity_To_Remote","manageability":"defaults_only","payload":{"start":{"object":"Opportunity"},"actionCalls":[{"name":"PushOpportunity","actionType":"PushToConnector"}]}},
    {"kind":"standard_flow","businessKey":"Account_To_Remote","manageability":"defaults_only","payload":{"start":{"object":"Account"},"actionCalls":[{"name":"PushAccount","actionType":"PushToConnector"}]}},
    {"kind":"connector_field_mapping","businessKey":"jira-like-sample/push-mappings","manageability":"locked","payload":[
      {"sourceObject":"Opportunity","targetObject":"remoteDeal","actionType":"PushToConnector","direction":"outbound","mappings":[{"sourceField":"Name","targetField":"dealname"}]},
      {"sourceObject":"Account","targetObject":"remoteCompany","actionType":"PushToConnector","direction":"outbound","mappings":[{"sourceField":"Name","targetField":"name"}]}
    ]},
    {"kind":"connector_push_contract","businessKey":"jira-like-sample/Account_To_Remote/PushAccount/Account/remote-company","manageability":"locked","payload":{
      "flowDeveloperName":"Account_To_Remote",
      "flowActionName":"PushAccount",
      "slotLineageId":"account-remote-sync",
      "sourceObject":"Account",
      "destinationSlotId":"remote-company",
      "profileOwnerPack":"hubspot-connector",
      "profileName":"default",
      "targetObject":"remoteCompany",
      "createPathTemplate":"/crm/v3/objects/companies",
      "updatePathTemplate":"/crm/v3/objects/companies/{remoteId}",
      "responseIdPath":"id",
      "remoteIdField":"RemoteCompanyId__c"
    }},
    {"kind":"connector_push_contract","businessKey":"jira-like-sample/Opportunity_To_Remote/PushOpportunity/Opportunity/remote-deal","manageability":"locked","payload":{
      "flowDeveloperName":"Opportunity_To_Remote",
      "flowActionName":"PushOpportunity",
      "slotLineageId":"opportunity-remote-sync",
      "sourceObject":"Opportunity",
      "destinationSlotId":"remote-deal",
      "profileOwnerPack":"hubspot-connector",
      "profileName":"default",
      "targetObject":"remoteDeal",
      "createPathTemplate":"/crm/v3/objects/deals",
      "updatePathTemplate":"/crm/v3/objects/deals/{remoteId}",
      "responseIdPath":"id",
      "remoteIdField":"RemoteDealId__c",
      "associations":[{
        "sourceLookupField":"AccountId",
        "sourceObject":"Account",
        "remoteIdField":"RemoteCompanyId__c",
        "targetActionSlotId":"account-remote-sync",
        "targetDestinationSlotId":"remote-company",
        "targetObject":"remoteCompany",
        "payloadField":"associations",
        "payloadMergeMode":"append",
        "inlinePhases":["create"],
        "payloadTemplate":{"to":{"id":"{toId}"}}
      }]
    }}
  ]`, `"dependencies": [],`))
	writeFile(t, root, "objects/crm.json", `[
  {
    "apiName": "Account",
    "label": "Account",
    "tableType": "standard_object",
    "category": "crm",
    "sharingModel": "Private",
    "enableHierarchySharing": false,
    "description": "Account",
    "fields": [
      {"apiName": "Name", "label": "Name", "type": "string", "required": true, "isNameField": true},
      {"apiName": "RemoteCompanyId__c", "label": "Remote Company ID", "type": "string"}
    ]
  },
  {
    "apiName": "Opportunity",
    "label": "Opportunity",
    "tableType": "standard_object",
    "category": "crm",
    "sharingModel": "Private",
    "enableHierarchySharing": false,
    "description": "Opportunity",
    "fields": [
      {"apiName": "Name", "label": "Name", "type": "string", "required": true, "isNameField": true},
      {"apiName": "RemoteDealId__c", "label": "Remote Deal ID", "type": "string"}
    ]
  }
]`)
	return root
}

func runtimeMetadataConnectorAssociationResolver() func(string) *models.ObjectMetadata {
	return func(apiName string) *models.ObjectMetadata {
		if strings.TrimSpace(apiName) != "Opportunity" {
			return nil
		}
		return &models.ObjectMetadata{
			APIName: "Opportunity",
			Fields: []models.FieldMetadata{{
				APIName:     "AccountId",
				Type:        constants.FieldTypeReference,
				ReferenceTo: []string{"Account"},
			}},
		}
	}
}

func expectRuntimeMetadataOpportunityAssociationLookupSchemaLoad(mock sqlmock.Sqlmock) {
	mock.ExpectQuery("SELECT .*FROM `_System_Object`.*`IsDeleted` = \\?").
		WithArgs(false).
		WillReturnRows(runtimeMetadataSystemObjectRows().
			AddRow(runtimeMetadataSystemObjectRowValues("obj_account", "Account")...).
			AddRow(runtimeMetadataSystemObjectRowValues("obj_opportunity", "Opportunity")...))
	mock.ExpectQuery("SELECT .*FROM `_System_Field`.*`IsDeleted` = \\?").
		WithArgs(false).
		WillReturnRows(sqlmock.NewRows(constants.ColsSystemField).
			AddRow(runtimeMetadataSystemFieldRowValues("obj_account", "Name", constants.FieldTypeString, nil)...).
			AddRow(runtimeMetadataSystemFieldRowValues("obj_account", "RemoteCompanyId__c", constants.FieldTypeString, nil)...).
			AddRow(runtimeMetadataSystemFieldRowValues("obj_opportunity", "AccountId", constants.FieldTypeReference, `["Account"]`)...))
	mock.ExpectQuery("SELECT .*FROM `_System_Relationship`").
		WillReturnRows(sqlmock.NewRows([]string{
			constants.FieldID,
			constants.FieldSysRelationship_ChildObjectAPIName,
			constants.FieldSysRelationship_ParentObjectAPIName,
			constants.FieldSysRelationship_FieldAPIName,
			constants.FieldSysRelationship_RelationshipName,
			constants.FieldSysRelationship_RelationshipType,
			constants.FieldSysRelationship_IsSharingParent,
			constants.FieldSysRelationship_CascadeDelete,
			constants.FieldSysRelationship_RestrictedDelete,
			constants.FieldSysRelationship_RelatedListLabel,
			constants.FieldSysRelationship_RelatedListFields,
			constants.FieldCreatedDate,
			constants.FieldLastModifiedDate,
		}))
}

func runtimeMetadataSystemObjectColumns() []string {
	cols := append([]string(nil), constants.ColsSystemObject...)
	return append(cols,
		constants.FieldSysObject_KeyPrefix,
		constants.FieldSysObject_DefaultChildRows,
		constants.FieldSysObject_DefaultChildRowsCustomized,
		constants.FieldSysObject_ChildCloneRulesCustomized,
		constants.FieldSysObject_ExclusiveSiblingFields,
		constants.FieldSysObject_ExclusiveSiblingFieldsCustomized,
		constants.FieldSysObject_ReadSource,
	)
}

func runtimeMetadataSystemObjectRows() *sqlmock.Rows {
	return sqlmock.NewRows(runtimeMetadataSystemObjectColumns())
}

func runtimeMetadataSystemObjectRowValues(id, apiName string) []driver.Value {
	values := []driver.Value{
		id,
		apiName,
		string(constants.TableTypeStandardObject),
		apiName,
		apiName + "s",
		nil,
		nil,
		nil,
		nil,
		false,
		false,
		true,
		string(constants.SharingModelPrivate),
		false,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		false,
		nil,
		false,
		nil,
		false,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		nil,
		false,
		false,
		nil,
		false,
		nil,
	}
	if len(values) != len(runtimeMetadataSystemObjectColumns()) {
		panic("runtimeMetadataSystemObjectRowValues out of sync with _System_Object columns")
	}
	return values
}

func runtimeMetadataSystemFieldRowValues(objectID, apiName string, fieldType constants.SchemaFieldType, referenceTo interface{}) []driver.Value {
	values := make([]driver.Value, len(constants.ColsSystemField))
	for i, col := range constants.ColsSystemField {
		switch col {
		case constants.FieldID:
			values[i] = "fld_" + strings.ToLower(apiName)
		case constants.FieldSysField_ObjectID:
			values[i] = objectID
		case constants.FieldSysField_APIName:
			values[i] = apiName
		case constants.FieldSysField_Label:
			values[i] = apiName
		case constants.FieldSysField_Type:
			values[i] = string(fieldType)
		case constants.FieldSysField_Createable, constants.FieldSysField_Updateable:
			values[i] = true
		case constants.FieldSysField_ReferenceTo:
			values[i] = referenceTo
		case constants.FieldSysField_OwnerKind:
			values[i] = "runtime"
		case constants.FieldSysField_Required,
			constants.FieldSysField_IsUnique,
			constants.FieldSysField_IsSystem,
			constants.FieldSysField_IsNameField,
			constants.FieldSysField_Indexed,
			constants.FieldSysField_TrackHistory,
			constants.FieldSysField_IsMasterDetail,
			constants.FieldSysField_IsPolymorphic,
			constants.FieldIsDeleted,
			constants.FieldSysField_IsCustomized:
			values[i] = false
		default:
			values[i] = nil
		}
	}
	return values
}

func TestLoadActiveInstalledRuntimeDeclarationsUsesSourceBackedFallbackWithAppliedHash(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	rawSourceDir := writeRuntimeTestPackSource(t, "jira-like-sample")
	sourceDir, err := filepath.EvalSymlinks(rawSourceDir)
	if err != nil {
		t.Fatalf("EvalSymlinks: %v", err)
	}
	pack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		sourceDir,
	)
	if err != nil {
		t.Fatalf("resolve source-backed pack: %v", err)
	}
	wantHash, err := PackPayloadHash(pack)
	if err != nil {
		t.Fatalf("payload hash: %v", err)
	}
	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"jira-like-sample",
			string(AppPack),
			wantHash,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			sourceDir,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
		))

	packList, err := LoadActiveInstalledRuntimeDeclarations(context.Background(), db)
	if err != nil {
		t.Fatalf("LoadActiveInstalledRuntimeDeclarations: %v", err)
	}
	if len(packList) != 1 {
		t.Fatalf("loaded pack count = %d, want 1", len(packList))
	}
	if _, ok := packList[0].(*SourceBackedPack); !ok {
		t.Fatalf("loaded pack type = %T, want *SourceBackedPack", packList[0])
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledRuntimeDeclarationsUsesSourceBackedFallbackForLegacyV0Row(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	rawSourceDir := writeRuntimeTestPackSource(t, "jira-like-sample")
	sourceDir, err := filepath.EvalSymlinks(rawSourceDir)
	if err != nil {
		t.Fatalf("EvalSymlinks: %v", err)
	}
	pack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		sourceDir,
	)
	if err != nil {
		t.Fatalf("resolve source-backed pack: %v", err)
	}
	wantHash, err := PackPayloadHash(pack)
	if err != nil {
		t.Fatalf("payload hash: %v", err)
	}
	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"jira-like-sample",
			string(AppPack),
			wantHash,
			nil,
			string(PackSourceKindLocalPath),
			sourceDir,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
		))

	packList, err := LoadActiveInstalledRuntimeDeclarations(context.Background(), db)
	if err != nil {
		t.Fatalf("LoadActiveInstalledRuntimeDeclarations: %v", err)
	}
	if len(packList) != 1 {
		t.Fatalf("loaded pack count = %d, want 1", len(packList))
	}
	if _, ok := packList[0].(*SourceBackedPack); !ok {
		t.Fatalf("loaded pack type = %T, want *SourceBackedPack", packList[0])
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledRuntimeDeclarationsRejectsMissingPersistedDeclarationWithoutAppliedHash(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	rawSourceDir := writeRuntimeTestPackSource(t, "jira-like-sample")
	sourceDir, err := filepath.EvalSymlinks(rawSourceDir)
	if err != nil {
		t.Fatalf("EvalSymlinks: %v", err)
	}
	_, err = ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		sourceDir,
	)
	if err != nil {
		t.Fatalf("resolve source-backed pack: %v", err)
	}
	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"jira-like-sample",
			string(AppPack),
			nil,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			sourceDir,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
		))

	_, err = LoadActiveInstalledRuntimeDeclarations(context.Background(), db)
	if err == nil || !strings.Contains(err.Error(), "missing persisted runtime declaration") {
		t.Fatalf("LoadActiveInstalledRuntimeDeclarations error = %v, want missing declaration error", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestRuntimeDeclarationContextForInstallUsesActivePlusInstallingPack(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	activeSourceDir := writeRuntimeValidationRulePackSource(t, "jira-like-addon")
	activePack, err := ResolveInstalledSourceBackedPack(
		"jira-like-addon",
		AppPack,
		string(PackSourceKindLocalPath),
		activeSourceDir,
	)
	if err != nil {
		t.Fatalf("resolve active pack: %v", err)
	}
	activeHash, err := PackPayloadHash(activePack)
	if err != nil {
		t.Fatalf("active payload hash: %v", err)
	}

	installingSourceDir := writeRuntimeTestPackSource(t, "jira-like-sample")
	installingPack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		installingSourceDir,
	)
	if err != nil {
		t.Fatalf("resolve installing pack: %v", err)
	}
	oldInstallingHash := PackPayloadHashV1Prefix + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
	currentInstallingHash, err := PackPayloadHash(installingPack)
	if err != nil {
		t.Fatalf("installing payload hash: %v", err)
	}
	if oldInstallingHash == currentInstallingHash {
		t.Fatal("test setup expected old installing hash to differ")
	}

	activeDecl, err := BuildPersistedRuntimeDeclaration(activePack)
	if err != nil {
		t.Fatalf("build active persisted runtime declaration: %v", err)
	}
	if activeDecl.StartupShallowDeclaredRowsJSON() == "[]" {
		t.Fatal("test setup expected active source pack to declare startup-shallow rows")
	}
	expectInstalledPackRuntimeDeclarationColumnsWithStartupShallow(mock, true, false)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields",
		}).AddRow(
			"jira-like-addon",
			string(AppPack),
			activeHash,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			activeSourceDir,
			nil,
			activeDecl.ObjectPayloadsJSON(),
			activeDecl.RelatedListRulesJSON(),
			activeDecl.SearchScopeOrderJSON(),
			activeDecl.SearchAllDefaultFieldsJSON(),
		).AddRow(
			"jira-like-sample",
			string(AppPack),
			oldInstallingHash,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			installingSourceDir,
			nil,
			nil,
			nil,
			nil,
			nil,
		))

	packList, err := RuntimeDeclarationContextForInstall(context.Background(), db, []Pack{installingPack})
	if err != nil {
		t.Fatalf("RuntimeDeclarationContextForInstall: %v", err)
	}
	var names []string
	for _, pack := range packList {
		names = append(names, pack.Name())
	}
	if want := []string{"jira-like-addon", "jira-like-sample"}; !reflect.DeepEqual(names, want) {
		t.Fatalf("declaration context names = %#v, want %#v", names, want)
	}
	if _, ok := packList[0].(*persistedRuntimePack); !ok {
		t.Fatalf("active declaration context pack type = %T, want *persistedRuntimePack", packList[0])
	}
	if gotHash, err := PackPayloadHash(packList[1]); err != nil {
		t.Fatalf("context installing hash: %v", err)
	} else if gotHash != currentInstallingHash {
		t.Fatalf("context installing hash = %q, want current source hash %q", gotHash, currentInstallingHash)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestRuntimeDeclarationContextForInstallUsesDBSchemaForActiveConnectorAssociationLookup(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	rawActiveSourceDir := writeRuntimeConnectorAssociationPackSource(t)
	activeSourceDir, err := filepath.EvalSymlinks(rawActiveSourceDir)
	if err != nil {
		t.Fatalf("EvalSymlinks active source: %v", err)
	}
	activePack, err := ResolveInstalledPack(
		InstalledPackResolverInput{
			Name:                  "jira-like-sample",
			Category:              AppPack,
			ManifestFormatVersion: SupportedManifestFormatVersion,
			SourceKind:            string(PackSourceKindLocalPath),
			SourceURI:             activeSourceDir,
		},
		InstalledPackResolverOptions{
			AllowBundledFallback: false,
			ObjectSchemaResolver: runtimeMetadataConnectorAssociationResolver(),
		},
	)
	if err != nil {
		t.Fatalf("resolve active pack with schema resolver: %v", err)
	}
	activeHash, err := PackPayloadHash(activePack)
	if err != nil {
		t.Fatalf("active payload hash: %v", err)
	}
	activeDecl, err := BuildPersistedRuntimeDeclaration(activePack)
	if err != nil {
		t.Fatalf("build active persisted runtime declaration: %v", err)
	}

	installingSourceDir := writeRuntimeTestPackSource(t, "unrelated-installing-pack")
	installingPack, err := ResolveInstalledSourceBackedPack(
		"unrelated-installing-pack",
		AppPack,
		string(PackSourceKindLocalPath),
		installingSourceDir,
	)
	if err != nil {
		t.Fatalf("resolve unrelated installing pack: %v", err)
	}

	expectInstalledPackRuntimeDeclarationColumnsWithStartupShallowAndFormulaBundles(mock, true, true, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
			"RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeFormulaFunctionBundles", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"jira-like-sample",
			string(AppPack),
			activeHash,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			activeSourceDir,
			nil,
			activeDecl.ObjectPayloadsJSON(),
			activeDecl.RelatedListRulesJSON(),
			activeDecl.SearchScopeOrderJSON(),
			activeDecl.SearchAllDefaultFieldsJSON(),
			activeDecl.FormulaFunctionBundlesJSON(),
			activeDecl.StartupShallowDeclaredRowsJSON(),
		))
	expectRuntimeMetadataOpportunityAssociationLookupSchemaLoad(mock)

	packList, err := RuntimeDeclarationContextForInstall(context.Background(), db, []Pack{installingPack})
	if err != nil {
		t.Fatalf("RuntimeDeclarationContextForInstall: %v", err)
	}
	var names []string
	for _, pack := range packList {
		names = append(names, pack.Name())
	}
	if want := []string{"jira-like-sample", "unrelated-installing-pack"}; !reflect.DeepEqual(names, want) {
		t.Fatalf("declaration context names = %#v, want %#v", names, want)
	}
	if _, ok := packList[0].(*persistedRuntimePack); !ok {
		t.Fatalf("active declaration context pack type = %T, want *persistedRuntimePack", packList[0])
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestActiveInstalledRuntimeSnapshotRejectsUnmintedAndAcceptsMintedEmpty(t *testing.T) {
	var typedNilPack *SourceBackedPack
	for _, snapshot := range []*ActiveInstalledRuntimeSnapshot{nil, {}} {
		_, err := snapshot.RuntimeFormulaFunctionBundlesForInstall(
			context.Background(), []Pack{typedNilPack}, nil, ComponentKindRegistrationSet{},
		)
		if err == nil || !strings.Contains(err.Error(), "not minted") {
			t.Fatalf("unminted snapshot error = %v, want fail closed", err)
		}
	}

	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	expectInstalledPackRuntimeDeclarationColumns(mock, false)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision\\s+FROM _System_InstalledPack").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
		}))
	snapshot, err := ReadActiveInstalledRuntimeSnapshot(context.Background(), db)
	if err != nil {
		t.Fatalf("read empty snapshot: %v", err)
	}
	bundles, err := snapshot.RuntimeFormulaFunctionBundlesForInstall(
		context.Background(), nil, nil, ComponentKindRegistrationSet{},
	)
	if err != nil {
		t.Fatalf("use empty snapshot: %v", err)
	}
	if len(bundles) != 0 {
		t.Fatalf("empty snapshot bundles = %#v, want none", bundles)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestActiveInstalledRuntimeSnapshotReusesFrozenPeersAcrossInstallingTuples(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	testActivePack := func(name, functionName string) (*SourceBackedPack, string, string, persistedRuntimeDeclaration) {
		manifest := validPackManifest(`[
			`+formulaFunctionBundleManifestComponent(strings.Replace(testFormulaFunctionBundleComponentPayload(), "PACK_PREFIX", functionName, 1))+`
		]`, ``)
		manifest = strings.Replace(manifest, `"name": "jira-like-sample"`, `"name": "`+name+`"`, 1)
		sourceDir := writePackFixture(t, name, manifest)
		pack, err := ResolveInstalledSourceBackedPack(
			name, AppPack, string(PackSourceKindLocalPath), sourceDir,
		)
		if err != nil {
			t.Fatalf("resolve active pack %s: %v", name, err)
		}
		hash, err := PackPayloadHash(pack)
		if err != nil {
			t.Fatalf("active payload hash %s: %v", name, err)
		}
		declaration, err := BuildPersistedRuntimeDeclaration(pack)
		if err != nil {
			t.Fatalf("build active persisted runtime declaration %s: %v", name, err)
		}
		return pack, sourceDir, hash, declaration
	}
	peerPack, peerSourceDir, peerHash, peerDecl := testActivePack("peer-pack", "PEER_PREFIX")
	replacedPack, replacedSourceDir, replacedHash, replacedDecl := testActivePack("release-pack", "OLD_PREFIX")

	testInstallingPack := func(functionName string) *SourceBackedPack {
		pack := testPersistedRuntimeSourceBackedPack(t, []ManifestComponent{{
			Kind:          ComponentKindFormulaFunctionBundle,
			BusinessKey:   "release-functions",
			Manageability: ManageabilityLocked,
			Payload:       []byte(strings.Replace(testFormulaFunctionBundleComponentPayload(), "PACK_PREFIX", functionName, 1)),
		}})
		pack.manifest.Name = "release-pack"
		return pack
	}
	currentPack := testInstallingPack("CURRENT_PREFIX")
	targetPack := testInstallingPack("TARGET_PREFIX")

	expectInstalledPackRuntimeDeclarationColumnsWithStartupShallowAndFormulaBundles(mock, true, true, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder, RuntimeSearchAllDefaultFields, RuntimeFormulaFunctionBundles, RuntimeStartupShallowDeclarations\\s+FROM _System_InstalledPack").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
			"RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeFormulaFunctionBundles", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			peerPack.Name(),
			string(AppPack),
			peerHash,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			peerSourceDir,
			nil,
			peerDecl.ObjectPayloadsJSON(),
			peerDecl.RelatedListRulesJSON(),
			peerDecl.SearchScopeOrderJSON(),
			peerDecl.SearchAllDefaultFieldsJSON(),
			peerDecl.FormulaFunctionBundlesJSON(),
			peerDecl.StartupShallowDeclaredRowsJSON(),
		).AddRow(
			replacedPack.Name(),
			string(AppPack),
			replacedHash,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			replacedSourceDir,
			nil,
			replacedDecl.ObjectPayloadsJSON(),
			replacedDecl.RelatedListRulesJSON(),
			replacedDecl.SearchScopeOrderJSON(),
			replacedDecl.SearchAllDefaultFieldsJSON(),
			replacedDecl.FormulaFunctionBundlesJSON(),
			replacedDecl.StartupShallowDeclaredRowsJSON(),
		))

	snapshot, err := ReadActiveInstalledRuntimeSnapshot(context.Background(), db)
	if err != nil {
		t.Fatalf("read active runtime snapshot: %v", err)
	}
	if snapshot.db != db {
		t.Fatalf("snapshot database = %p, want originating database %p", snapshot.db, db)
	}
	functionNames := func(installing Pack) []string {
		bundles, err := snapshot.RuntimeFormulaFunctionBundlesForInstall(
			context.Background(), []Pack{installing}, nil, ComponentKindRegistrationSet{},
		)
		if err != nil {
			t.Fatalf("runtime formula context for %s: %v", installing.Name(), err)
		}
		var names []string
		for _, bundle := range bundles {
			for _, fn := range bundle.Functions {
				names = append(names, fn.Name)
			}
		}
		return names
	}
	if got, want := functionNames(currentPack), []string{"PEER_PREFIX", "CURRENT_PREFIX"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("current formula context = %#v, want %#v", got, want)
	}
	if got, want := functionNames(targetPack), []string{"PEER_PREFIX", "TARGET_PREFIX"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("target formula context = %#v, want %#v", got, want)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestNormalizedRuntimeFormulaInstallInputsDeduplicatesInstallingAndOverrides(t *testing.T) {
	installing, overrides := normalizedRuntimeFormulaInstallInputs(
		[]Pack{
			runtimeFormulaFunctionBundleInstallPack{name: " Requested "},
			runtimeFormulaFunctionBundleInstallPack{name: "requested"},
		},
		map[string]Pack{
			"requested": runtimeFormulaFunctionBundleInstallPack{name: "REQUESTED"},
			"peer-a":    runtimeFormulaFunctionBundleInstallPack{name: " Peer "},
			"peer-b":    runtimeFormulaFunctionBundleInstallPack{name: "peer"},
		},
	)
	if len(installing) != 1 || strings.ToLower(installing[0].Name()) != "requested" {
		t.Fatalf("normalized installing = %#v, want one requested pack", installing)
	}
	if len(overrides) != 1 || overrides["peer"] == nil {
		t.Fatalf("normalized overrides = %#v, want one peer override", overrides)
	}
}

func TestRuntimeFormulaFunctionBundlesForInstallSourceWithOverridesAndComponentKindRegistrationSetDeduplicatesDBRowNames(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	manifest := strings.Replace(validPackManifest(`[
		`+formulaFunctionBundleManifestComponent(strings.Replace(testFormulaFunctionBundleComponentPayload(), "PACK_PREFIX", "CANDIDATE_PREFIX", 1))+`
	]`, ``), `"name": "jira-like-sample"`, `"name": "Requested"`, 1)
	sourceDir := writePackFixture(t, "requested-candidate", manifest)
	sourceReport := ValidatePackSourceWithOptions("local_path:"+sourceDir, testPackSourceOptions(t))
	if sourceReport.HasErrors() || sourceReport.Source == nil {
		t.Fatalf("candidate source diagnostics: %s", sourceReport.Error())
	}

	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
			"RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow("requested", string(AppPack), nil, SupportedManifestFormatVersion, nil, nil, nil, nil, nil, nil, nil, nil))

	bundles, err := RuntimeFormulaFunctionBundlesForInstallSourceWithOverridesAndComponentKindRegistrationSet(
		context.Background(), db, sourceReport.Source, nil, ComponentKindRegistrationSet{},
	)
	if err != nil {
		t.Fatalf("registration-aware source bundle context: %v", err)
	}
	if len(bundles) != 1 || len(bundles[0].Functions) != 1 || bundles[0].Functions[0].Name != "CANDIDATE_PREFIX" {
		t.Fatalf("effective bundles = %#v, want one CANDIDATE_PREFIX bundle", bundles)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestRuntimeFormulaFunctionBundlesForInstallWithOverridesAndComponentKindRegistrationSetUsesLegacySourceBridge(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	managedKind := cp.ComponentKind("registered_formula_provider_test")
	registration := testComponentKindRegistration(managedKind, "RegisteredFormulaProvider")
	registration.BuildHandler = func(ctx cp.ComponentBuildContext, _ cp.ComponentBuildRequest) error {
		return ctx.AddManagedComponent(cp.ComponentBuildManagedComponent{ExpectedProjection: []byte(`{"rows":[]}`)})
	}
	registrations, err := NewComponentKindRegistrationSet([]cp.ComponentKindRegistration{registration})
	if err != nil {
		t.Fatal(err)
	}
	providerManifest := strings.Replace(validPackManifest(`[
		`+formulaFunctionBundleManifestComponent(testFormulaFunctionBundleComponentPayload())+`,
		{
			"kind": "registered_formula_provider_test",
			"businessKey": "Provider/One",
			"manageability": "locked",
			"payload": {}
		}
	]`, ``), `"name": "jira-like-sample"`, `"name": "registered-formula-provider"`, 1)
	providerSourceDir := writePackFixture(t, "registered-formula-provider", providerManifest)
	providerReport := ValidateInstallablePackSourceWithComponentKindRegistrationSet(
		"local_path:"+providerSourceDir, testPackSourceOptions(t), registrations,
	)
	if providerReport.HasErrors() || providerReport.Pack == nil {
		t.Fatalf("provider source diagnostics: %s", providerReport.Error())
	}
	providerPack := providerReport.Pack
	providerHash, err := PackPayloadHash(providerPack)
	if err != nil {
		t.Fatalf("provider payload hash: %v", err)
	}
	providerDecl, err := BuildPersistedRuntimeDeclaration(providerPack)
	if err != nil {
		t.Fatalf("provider runtime declaration: %v", err)
	}

	expectInstalledPackRuntimeDeclarationColumnsWithStartupShallowAndFormulaBundles(mock, true, true, false)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
			"RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			providerPack.Name(), string(AppPack), providerHash, SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath), providerSourceDir, nil,
			providerDecl.ObjectPayloadsJSON(), providerDecl.RelatedListRulesJSON(), providerDecl.SearchScopeOrderJSON(),
			providerDecl.SearchAllDefaultFieldsJSON(), providerDecl.StartupShallowDeclaredRowsJSON(),
		))

	snapshot, err := ReadActiveInstalledRuntimeSnapshot(context.Background(), db)
	if err != nil {
		t.Fatalf("read legacy active runtime snapshot: %v", err)
	}
	bundles, err := snapshot.RuntimeFormulaFunctionBundlesForInstall(
		context.Background(), nil, nil, registrations,
	)
	if err != nil {
		t.Fatalf("registration-aware legacy formula bundle bridge: %v", err)
	}
	if len(bundles) != 1 || len(bundles[0].Functions) != 1 || bundles[0].Functions[0].Name != "PACK_PREFIX" {
		t.Fatalf("legacy bridge bundles = %#v, want PACK_PREFIX", bundles)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestRuntimeDeclarationContextForInstallExcludesReplacedPackFormulaBundlesFromValidation(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	providerManifest := strings.Replace(validPackManifest(`[
		`+formulaFunctionBundleManifestComponent(testFormulaFunctionBundleComponentPayload())+`
	]`, `"dependencies": [],`), `"name": "jira-like-sample"`, `"name": "formula-provider-pack"`, 1)
	providerSourceDir := writePackFixture(t, "formula-provider-pack", providerManifest)
	activeProviderPack, err := ResolveInstalledSourceBackedPack(
		"formula-provider-pack",
		AppPack,
		string(PackSourceKindLocalPath),
		providerSourceDir,
	)
	if err != nil {
		t.Fatalf("resolve active provider pack: %v", err)
	}
	activeProviderHash, err := PackPayloadHash(activeProviderPack)
	if err != nil {
		t.Fatalf("active provider payload hash: %v", err)
	}
	activeProviderDecl, err := BuildPersistedRuntimeDeclaration(activeProviderPack)
	if err != nil {
		t.Fatalf("active provider runtime declaration: %v", err)
	}

	consumerManifest := strings.Replace(validPackManifest(`[
		{
			"kind": "standard_flow",
			"businessKey": "IssueFlow",
			"manageability": "defaults_only",
			"payload": `+testFlowFilterFormulaWithBundlePayload()+`
		},
		{
			"kind": "object_spec",
			"businessKey": "issue",
			"manageability": "locked",
			"payload": `+testObjectSpecPayloadWithBundleWriteFormulaAndLockRule()+`
		}
	]`, `"dependencies": [{"name": "formula-provider-pack", "versionConstraint": ">=0.1.0"}],`), `"name": "jira-like-sample"`, `"name": "formula-consumer-pack"`, 1)
	consumerSourceDir := writePackFixture(t, "formula-consumer-pack", consumerManifest)
	consumerSourceReport := ValidatePackSourceWithOptions("local_path:"+consumerSourceDir, testPackSourceOptions(t))
	if consumerSourceReport.HasErrors() {
		t.Fatalf("consumer source diagnostics:\n%s", consumerSourceReport.Error())
	}
	consumerPack, err := NewSourceBackedPack(
		consumerSourceReport.Source,
		WithRuntimeFormulaFunctionBundlesForValidation(activeProviderPack.RuntimeFormulaFunctionBundles()),
	)
	if err != nil {
		t.Fatalf("construct active consumer pack: %v", err)
	}
	consumerHash, err := PackPayloadHash(consumerPack)
	if err != nil {
		t.Fatalf("consumer payload hash: %v", err)
	}
	consumerDecl, err := BuildPersistedRuntimeDeclaration(consumerPack)
	if err != nil {
		t.Fatalf("consumer runtime declaration: %v", err)
	}

	candidateManifest := strings.Replace(validPackManifest(`[
		`+formulaFunctionBundleManifestComponent(testFormulaFunctionBundleComponentPayload())+`,
		{
			"kind": "standard_flow",
			"businessKey": "IssueFlow",
			"manageability": "defaults_only",
			"payload": `+testFlowFilterFormulaWithBundlePayload()+`
		},
		{
			"kind": "object_spec",
			"businessKey": "issue",
			"manageability": "locked",
			"payload": `+testObjectSpecPayloadWithBundleWriteFormulaAndLockRule()+`
		}
	]`, `"dependencies": [],`), `"name": "jira-like-sample"`, `"name": "formula-provider-pack"`, 1)
	candidateSourceDir := writePackFixture(t, "formula-provider-pack-candidate", candidateManifest)
	candidatePack, err := ResolveInstalledSourceBackedPack(
		"formula-provider-pack",
		AppPack,
		string(PackSourceKindLocalPath),
		candidateSourceDir,
	)
	if err != nil {
		t.Fatalf("resolve candidate provider pack: %v", err)
	}

	expectInstalledPackRuntimeDeclarationColumnsWithStartupShallowAndFormulaBundles(mock, true, true, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder, RuntimeSearchAllDefaultFields, RuntimeFormulaFunctionBundles, RuntimeStartupShallowDeclarations\\s+FROM _System_InstalledPack").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
			"RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeFormulaFunctionBundles", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			consumerPack.Name(),
			string(AppPack),
			consumerHash,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			consumerSourceDir,
			nil,
			consumerDecl.ObjectPayloadsJSON(),
			consumerDecl.RelatedListRulesJSON(),
			consumerDecl.SearchScopeOrderJSON(),
			consumerDecl.SearchAllDefaultFieldsJSON(),
			consumerDecl.FormulaFunctionBundlesJSON(),
			consumerDecl.StartupShallowDeclaredRowsJSON(),
		).AddRow(
			activeProviderPack.Name(),
			string(AppPack),
			activeProviderHash,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			providerSourceDir,
			nil,
			activeProviderDecl.ObjectPayloadsJSON(),
			activeProviderDecl.RelatedListRulesJSON(),
			activeProviderDecl.SearchScopeOrderJSON(),
			activeProviderDecl.SearchAllDefaultFieldsJSON(),
			activeProviderDecl.FormulaFunctionBundlesJSON(),
			activeProviderDecl.StartupShallowDeclaredRowsJSON(),
		))

	packList, err := RuntimeDeclarationContextForInstall(context.Background(), db, []Pack{candidatePack})
	if err != nil {
		t.Fatalf("RuntimeDeclarationContextForInstall replacing formula provider: %v", err)
	}
	var names []string
	for _, pack := range packList {
		names = append(names, pack.Name())
	}
	if want := []string{"formula-consumer-pack", "formula-provider-pack"}; !reflect.DeepEqual(names, want) {
		t.Fatalf("declaration context names = %#v, want %#v", names, want)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestRuntimeDeclarationContextForInstallUsesOverrideFormulaBundlesForValidation(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	activeProviderManifest := strings.Replace(validPackManifest(`[
		`+formulaFunctionBundleManifestComponent(strings.Replace(testFormulaFunctionBundleComponentPayload(), "PACK_PREFIX", "OLD_PREFIX", 1))+`
	]`, `"dependencies": [],`), `"name": "jira-like-sample"`, `"name": "formula-provider-pack"`, 1)
	activeProviderSourceDir := writePackFixture(t, "formula-provider-pack-active", activeProviderManifest)
	activeProviderPack, err := ResolveInstalledSourceBackedPack(
		"formula-provider-pack",
		AppPack,
		string(PackSourceKindLocalPath),
		activeProviderSourceDir,
	)
	if err != nil {
		t.Fatalf("resolve active provider pack: %v", err)
	}
	activeProviderHash, err := PackPayloadHash(activeProviderPack)
	if err != nil {
		t.Fatalf("active provider payload hash: %v", err)
	}
	activeProviderDecl, err := BuildPersistedRuntimeDeclaration(activeProviderPack)
	if err != nil {
		t.Fatalf("active provider runtime declaration: %v", err)
	}

	overrideProviderManifest := strings.Replace(validPackManifest(`[
		`+formulaFunctionBundleManifestComponent(testFormulaFunctionBundleComponentPayload())+`
	]`, `"dependencies": [],`), `"name": "jira-like-sample"`, `"name": "formula-provider-pack"`, 1)
	overrideProviderSourceDir := writePackFixture(t, "formula-provider-pack-override", overrideProviderManifest)
	overrideProviderPack, err := ResolveInstalledSourceBackedPack(
		"formula-provider-pack",
		AppPack,
		string(PackSourceKindLocalPath),
		overrideProviderSourceDir,
	)
	if err != nil {
		t.Fatalf("resolve override provider pack: %v", err)
	}

	consumerManifest := strings.Replace(validPackManifest(`[
		{
			"kind": "standard_flow",
			"businessKey": "IssueFlow",
			"manageability": "defaults_only",
			"payload": `+testFlowFilterFormulaWithBundlePayload()+`
		},
		{
			"kind": "object_spec",
			"businessKey": "issue",
			"manageability": "locked",
			"payload": `+testObjectSpecPayloadWithBundleWriteFormulaAndLockRule()+`
		}
	]`, `"dependencies": [{"name": "formula-provider-pack", "versionConstraint": ">=0.1.0"}],`), `"name": "jira-like-sample"`, `"name": "formula-consumer-pack"`, 1)
	consumerSourceDir := writePackFixture(t, "formula-consumer-pack", consumerManifest)
	consumerSourceReport := ValidatePackSourceWithOptions("local_path:"+consumerSourceDir, testPackSourceOptions(t))
	if consumerSourceReport.HasErrors() {
		t.Fatalf("consumer source diagnostics:\n%s", consumerSourceReport.Error())
	}
	consumerPack, err := NewSourceBackedPack(
		consumerSourceReport.Source,
		WithRuntimeFormulaFunctionBundlesForValidation(overrideProviderPack.RuntimeFormulaFunctionBundles()),
	)
	if err != nil {
		t.Fatalf("construct active consumer pack: %v", err)
	}
	consumerHash, err := PackPayloadHash(consumerPack)
	if err != nil {
		t.Fatalf("consumer payload hash: %v", err)
	}
	consumerDecl, err := BuildPersistedRuntimeDeclaration(consumerPack)
	if err != nil {
		t.Fatalf("consumer runtime declaration: %v", err)
	}

	expectInstalledPackRuntimeDeclarationColumnsWithStartupShallowAndFormulaBundles(mock, true, true, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder, RuntimeSearchAllDefaultFields, RuntimeFormulaFunctionBundles, RuntimeStartupShallowDeclarations\\s+FROM _System_InstalledPack").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
			"RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeFormulaFunctionBundles", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			consumerPack.Name(),
			string(AppPack),
			consumerHash,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			consumerSourceDir,
			nil,
			consumerDecl.ObjectPayloadsJSON(),
			consumerDecl.RelatedListRulesJSON(),
			consumerDecl.SearchScopeOrderJSON(),
			consumerDecl.SearchAllDefaultFieldsJSON(),
			consumerDecl.FormulaFunctionBundlesJSON(),
			consumerDecl.StartupShallowDeclaredRowsJSON(),
		).AddRow(
			activeProviderPack.Name(),
			string(AppPack),
			activeProviderHash,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			activeProviderSourceDir,
			nil,
			activeProviderDecl.ObjectPayloadsJSON(),
			activeProviderDecl.RelatedListRulesJSON(),
			activeProviderDecl.SearchScopeOrderJSON(),
			activeProviderDecl.SearchAllDefaultFieldsJSON(),
			activeProviderDecl.FormulaFunctionBundlesJSON(),
			activeProviderDecl.StartupShallowDeclaredRowsJSON(),
		))

	packList, err := runtimeDeclarationContextForInstall(context.Background(), db, nil, map[string]Pack{
		"formula-provider-pack": overrideProviderPack,
	})
	if err != nil {
		t.Fatalf("runtimeDeclarationContextForInstall with formula bundle override: %v", err)
	}
	var names []string
	for _, pack := range packList {
		names = append(names, pack.Name())
	}
	if want := []string{"formula-consumer-pack", "formula-provider-pack"}; !reflect.DeepEqual(names, want) {
		t.Fatalf("declaration context names = %#v, want %#v", names, want)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledRuntimeDeclarationsUsesActiveFormulaBundlesForSourceVerification(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	providerManifest := strings.Replace(validPackManifest(`[
		`+formulaFunctionBundleManifestComponent(testFormulaFunctionBundleComponentPayload())+`
	]`, `"dependencies": [],`), `"name": "jira-like-sample"`, `"name": "formula-provider-pack"`, 1)
	providerSourceDir := writePackFixture(t, "formula-provider-pack", providerManifest)
	providerPack, err := ResolveInstalledSourceBackedPack(
		"formula-provider-pack",
		AppPack,
		string(PackSourceKindLocalPath),
		providerSourceDir,
	)
	if err != nil {
		t.Fatalf("resolve provider source-backed pack: %v", err)
	}
	providerHash, err := PackPayloadHash(providerPack)
	if err != nil {
		t.Fatalf("provider payload hash: %v", err)
	}
	providerDecl, err := BuildPersistedRuntimeDeclaration(providerPack)
	if err != nil {
		t.Fatalf("provider runtime declaration: %v", err)
	}

	consumerManifest := strings.Replace(validPackManifest(`[
		{
			"kind": "standard_flow",
			"businessKey": "IssueFlow",
			"manageability": "defaults_only",
			"payload": `+testFlowFilterFormulaWithBundlePayload()+`
		},
		{
			"kind": "object_spec",
			"businessKey": "issue",
			"manageability": "locked",
			"payload": `+testObjectSpecPayloadWithBundleWriteFormulaAndLockRule()+`
		}
	]`, `"dependencies": [{"name": "formula-provider-pack", "versionConstraint": ">=0.1.0"}],`), `"name": "jira-like-sample"`, `"name": "formula-consumer-pack"`, 1)
	consumerSourceDir := writePackFixture(t, "formula-consumer-pack", consumerManifest)
	consumerSourceReport := ValidatePackSourceWithOptions("local_path:"+consumerSourceDir, testPackSourceOptions(t))
	if consumerSourceReport.HasErrors() {
		t.Fatalf("consumer source diagnostics:\n%s", consumerSourceReport.Error())
	}
	consumerPack, err := NewSourceBackedPack(
		consumerSourceReport.Source,
		WithRuntimeFormulaFunctionBundlesForValidation(providerPack.RuntimeFormulaFunctionBundles()),
	)
	if err != nil {
		t.Fatalf("construct consumer source-backed pack with active provider bundles: %v", err)
	}
	consumerHash, err := PackPayloadHash(consumerPack)
	if err != nil {
		t.Fatalf("consumer payload hash: %v", err)
	}
	consumerDecl, err := BuildPersistedRuntimeDeclaration(consumerPack)
	if err != nil {
		t.Fatalf("consumer runtime declaration: %v", err)
	}

	expectInstalledPackRuntimeDeclarationColumnsWithStartupShallowAndFormulaBundles(mock, true, true, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder, RuntimeSearchAllDefaultFields, RuntimeFormulaFunctionBundles, RuntimeStartupShallowDeclarations\\s+FROM _System_InstalledPack").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
			"RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeFormulaFunctionBundles", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			consumerPack.Name(),
			string(AppPack),
			consumerHash,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			consumerSourceDir,
			nil,
			consumerDecl.ObjectPayloadsJSON(),
			consumerDecl.RelatedListRulesJSON(),
			consumerDecl.SearchScopeOrderJSON(),
			consumerDecl.SearchAllDefaultFieldsJSON(),
			consumerDecl.FormulaFunctionBundlesJSON(),
			consumerDecl.StartupShallowDeclaredRowsJSON(),
		).AddRow(
			providerPack.Name(),
			string(AppPack),
			providerHash,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			providerSourceDir,
			nil,
			providerDecl.ObjectPayloadsJSON(),
			providerDecl.RelatedListRulesJSON(),
			providerDecl.SearchScopeOrderJSON(),
			providerDecl.SearchAllDefaultFieldsJSON(),
			providerDecl.FormulaFunctionBundlesJSON(),
			providerDecl.StartupShallowDeclaredRowsJSON(),
		))

	packList, err := LoadActiveInstalledRuntimeDeclarations(context.Background(), db)
	if err != nil {
		t.Fatalf("LoadActiveInstalledRuntimeDeclarations with dependency bundle consumer: %v", err)
	}
	if len(packList) != 2 {
		t.Fatalf("loaded pack count = %d, want 2", len(packList))
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestRuntimeDeclarationContextForInstallFallsBackToSourceIdentityWhenActivePersistedDeclarationMissing(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	activeSourceDir := writeRuntimeTestPackSource(t, "jira-like-addon")
	activePack, err := ResolveInstalledSourceBackedPack(
		"jira-like-addon",
		AppPack,
		string(PackSourceKindLocalPath),
		activeSourceDir,
	)
	if err != nil {
		t.Fatalf("resolve active pack: %v", err)
	}
	activeHash, err := PackPayloadHash(activePack)
	if err != nil {
		t.Fatalf("active payload hash: %v", err)
	}

	installingSourceDir := writeRuntimeTestPackSource(t, "jira-like-sample")
	installingPack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		installingSourceDir,
	)
	if err != nil {
		t.Fatalf("resolve installing pack: %v", err)
	}

	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"jira-like-addon",
			string(AppPack),
			activeHash,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			activeSourceDir,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
		))

	packList, err := RuntimeDeclarationContextForInstall(context.Background(), db, []Pack{installingPack})
	if err != nil {
		t.Fatalf("RuntimeDeclarationContextForInstall: %v", err)
	}
	if len(packList) != 2 {
		t.Fatalf("context pack count = %d, want 2", len(packList))
	}
	if _, ok := packList[0].(*SourceBackedPack); !ok {
		t.Fatalf("active declaration context pack type = %T, want *SourceBackedPack", packList[0])
	}
	if packList[1].Name() != "jira-like-sample" {
		t.Fatalf("installing pack name = %q, want jira-like-sample", packList[1].Name())
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestRuntimeDeclarationContextForInstallFallsBackToSourceIdentityForLegacyV0ActivePeer(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	activeSourceDir := writeRuntimeTestPackSource(t, "jira-like-addon")
	activePack, err := ResolveInstalledSourceBackedPack(
		"jira-like-addon",
		AppPack,
		string(PackSourceKindLocalPath),
		activeSourceDir,
	)
	if err != nil {
		t.Fatalf("resolve active pack: %v", err)
	}
	activeHash, err := PackPayloadHash(activePack)
	if err != nil {
		t.Fatalf("active payload hash: %v", err)
	}

	installingSourceDir := writeRuntimeTestPackSource(t, "jira-like-sample")
	installingPack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		installingSourceDir,
	)
	if err != nil {
		t.Fatalf("resolve installing pack: %v", err)
	}

	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"jira-like-addon",
			string(AppPack),
			activeHash,
			nil,
			string(PackSourceKindLocalPath),
			activeSourceDir,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
		))

	packList, err := RuntimeDeclarationContextForInstall(context.Background(), db, []Pack{installingPack})
	if err != nil {
		t.Fatalf("RuntimeDeclarationContextForInstall: %v", err)
	}
	if len(packList) != 2 {
		t.Fatalf("context pack count = %d, want 2", len(packList))
	}
	if _, ok := packList[0].(*SourceBackedPack); !ok {
		t.Fatalf("active declaration context pack type = %T, want *SourceBackedPack", packList[0])
	}
	if packList[1].Name() != "jira-like-sample" {
		t.Fatalf("installing pack name = %q, want jira-like-sample", packList[1].Name())
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestRuntimeDeclarationContextForInstallUsesOverrideForStaleActivePeer(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	activeSourceDir := writeRuntimeTestPackSource(t, "jira-like-addon")
	activePack, err := ResolveInstalledSourceBackedPack(
		"jira-like-addon",
		AppPack,
		string(PackSourceKindLocalPath),
		activeSourceDir,
	)
	if err != nil {
		t.Fatalf("resolve active pack: %v", err)
	}

	installingSourceDir := writeRuntimeTestPackSource(t, "jira-like-sample")
	installingPack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		installingSourceDir,
	)
	if err != nil {
		t.Fatalf("resolve installing pack: %v", err)
	}

	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision", "RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"jira-like-addon",
			string(AppPack),
			PackPayloadHashV1Prefix+"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			activeSourceDir,
			nil,
			nil,
			nil,
			nil,
			nil,
			nil,
		))

	packList, err := runtimeDeclarationContextForInstall(context.Background(), db, []Pack{installingPack}, map[string]Pack{
		"jira-like-addon": activePack,
	})
	if err != nil {
		t.Fatalf("runtimeDeclarationContextForInstall with override: %v", err)
	}
	var names []string
	for _, pack := range packList {
		names = append(names, pack.Name())
	}
	if want := []string{"jira-like-addon", "jira-like-sample"}; !reflect.DeepEqual(names, want) {
		t.Fatalf("declaration context names = %#v, want %#v", names, want)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledRuntimeDeclarationsUsesSourceBackedFallbackWhenRuntimeColumnsAbsent(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	rawSourceDir := writeRuntimeTestPackSource(t, "jira-like-sample")
	sourceDir, err := filepath.EvalSymlinks(rawSourceDir)
	if err != nil {
		t.Fatalf("EvalSymlinks: %v", err)
	}
	pack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		sourceDir,
	)
	if err != nil {
		t.Fatalf("resolve source-backed pack: %v", err)
	}
	wantHash, err := PackPayloadHash(pack)
	if err != nil {
		t.Fatalf("payload hash: %v", err)
	}

	expectInstalledPackRuntimeDeclarationColumns(mock, false)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision\\s+FROM _System_InstalledPack").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
		}).AddRow(
			"jira-like-sample",
			string(AppPack),
			wantHash,
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			sourceDir,
			nil,
		))

	packList, err := LoadActiveInstalledRuntimeDeclarations(context.Background(), db)
	if err != nil {
		t.Fatalf("LoadActiveInstalledRuntimeDeclarations: %v", err)
	}
	if len(packList) != 1 {
		t.Fatalf("loaded pack count = %d, want 1", len(packList))
	}
	if _, ok := packList[0].(*SourceBackedPack); !ok {
		t.Fatalf("loaded pack type = %T, want *SourceBackedPack", packList[0])
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledPersistedRuntimeDeclarationsUsesPersistedDeclarationsWithoutSourceIdentity(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	pack := runtimeStartupShallowPersistedCompatPack{}
	decl, err := BuildPersistedRuntimeDeclaration(pack)
	if err != nil {
		t.Fatalf("build persisted runtime declaration: %v", err)
	}

	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder, RuntimeSearchAllDefaultFields, RuntimeStartupShallowDeclarations\\s+FROM _System_InstalledPack").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
			"RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			pack.Name(),
			string(AppPack),
			nil,
			nil,
			nil,
			nil,
			nil,
			decl.ObjectPayloadsJSON(),
			decl.RelatedListRulesJSON(),
			decl.SearchScopeOrderJSON(),
			decl.SearchAllDefaultFieldsJSON(),
			decl.StartupShallowDeclaredRowsJSON(),
		))

	packList, err := LoadActiveInstalledPersistedRuntimeDeclarations(context.Background(), db)
	if err != nil {
		t.Fatalf("LoadActiveInstalledPersistedRuntimeDeclarations: %v", err)
	}
	if len(packList) != 1 {
		t.Fatalf("loaded pack count = %d, want 1", len(packList))
	}
	if _, ok := packList[0].(*persistedRuntimePack); !ok {
		t.Fatalf("loaded pack type = %T, want *persistedRuntimePack", packList[0])
	}
	if packList[0].Name() != pack.Name() {
		t.Fatalf("loaded pack name = %q, want %q", packList[0].Name(), pack.Name())
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledPersistedRelatedListRulesUsesPersistedSnapshot(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	want := metadata.RelatedListRule{
		Key: "account-files", ParentObjectAPI: "Account",
		ChildObjectAPI: "ContentDocumentLink", LookupField: "LinkedEntityId",
		Label: "Files", Fields: []string{"Title"},
		Resolution: &metadata.RelatedListResolution{
			Query: metadata.ResolvedQueryTarget{
				ObjectApiName: "ContentVersion", LookupField: "FirstPublishLocationId",
			},
		},
	}
	rulesJSON, err := json.Marshal([]metadata.RelatedListRule{want})
	if err != nil {
		t.Fatal(err)
	}
	expectInstalledPackRuntimeDeclarationColumns(mock, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder, RuntimeSearchAllDefaultFields, RuntimeStartupShallowDeclarations\\s+FROM _System_InstalledPack").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
			"RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			"crm", string(AppPack), nil, nil, nil, nil, nil,
			"[]", string(rulesJSON), "[]", "[]", "[]",
		))

	got, err := LoadActiveInstalledPersistedRelatedListRulesWithExecutor(context.Background(), db)
	if err != nil {
		t.Fatalf("load persisted related-list rules: %v", err)
	}
	if !reflect.DeepEqual(got, []metadata.RelatedListRule{want}) {
		t.Fatalf("persisted related-list rules = %#v, want %#v", got, []metadata.RelatedListRule{want})
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestLoadActiveInstalledPersistedRuntimeDeclarationsRestoresFormulaFunctionBundles(t *testing.T) {
	if err := formula.RegisterRuntimeFunctionBundles(nil); err != nil {
		t.Fatalf("clear runtime formula bundles before test: %v", err)
	}
	t.Cleanup(func() {
		_ = formula.RegisterRuntimeFunctionBundles(nil)
	})

	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	pack := testPersistedRuntimeSourceBackedPack(t, []ManifestComponent{{
		Kind:          ComponentKindFormulaFunctionBundle,
		BusinessKey:   "probe-functions",
		Manageability: ManageabilityLocked,
		Payload:       []byte(testFormulaFunctionBundleComponentPayload()),
	}})
	decl, err := BuildPersistedRuntimeDeclaration(pack)
	if err != nil {
		t.Fatalf("build persisted runtime declaration: %v", err)
	}

	expectInstalledPackRuntimeDeclarationColumnsWithStartupShallowAndFormulaBundles(mock, true, true, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder, RuntimeSearchAllDefaultFields, RuntimeFormulaFunctionBundles, RuntimeStartupShallowDeclarations\\s+FROM _System_InstalledPack").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
			"RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeFormulaFunctionBundles", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			pack.Name(),
			string(AppPack),
			nil,
			nil,
			nil,
			nil,
			nil,
			decl.ObjectPayloadsJSON(),
			decl.RelatedListRulesJSON(),
			decl.SearchScopeOrderJSON(),
			decl.SearchAllDefaultFieldsJSON(),
			decl.FormulaFunctionBundlesJSON(),
			decl.StartupShallowDeclaredRowsJSON(),
		))

	packList, err := LoadActiveInstalledPersistedRuntimeDeclarations(context.Background(), db)
	if err != nil {
		t.Fatalf("LoadActiveInstalledPersistedRuntimeDeclarations: %v", err)
	}
	if err := RegisterRuntimeMetadataForPacks(context.Background(), nil, packList); err != nil {
		t.Fatalf("RegisterRuntimeMetadataForPacks from persisted installed declaration: %v", err)
	}
	engine := formula.NewEngine()
	got, err := engine.Evaluate(`PACK_PREFIX(Name)`, &formula.Context{Record: map[string]interface{}{"Name": "alpha"}})
	if err != nil {
		t.Fatalf("Evaluate PACK_PREFIX from persisted installed runtime declaration: %v", err)
	}
	if got != "alpha-probe" {
		t.Fatalf("Evaluate PACK_PREFIX from persisted installed runtime declaration = %v, want alpha-probe", got)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestResolveInstalledPackDisablesBundledFallbackWhenSourceRequired(t *testing.T) {
	_, err := ResolveInstalledPack(
		InstalledPackResolverInput{
			Name:     "salesforce-crm-standard",
			Category: AppPack,
		},
		InstalledPackResolverOptions{AllowBundledFallback: false},
	)
	if err == nil {
		t.Fatal("ResolveInstalledPack = nil, want missing source identity error")
	}
	if got := err.Error(); got != "installed pack salesforce-crm-standard has no source identity" {
		t.Fatalf("ResolveInstalledPack error = %q", got)
	}
}

func TestResolveInstalledPackRejectsPartialSourceIdentity(t *testing.T) {
	_, err := ResolveInstalledPack(
		InstalledPackResolverInput{
			Name:       "jira-like-sample",
			Category:   AppPack,
			SourceKind: string(PackSourceKindLocalPath),
		},
		InstalledPackResolverOptions{AllowBundledFallback: true},
	)
	if err == nil {
		t.Fatal("ResolveInstalledPack = nil, want partial source identity error")
	}
	if got := err.Error(); got != "resolve installed source-backed pack jira-like-sample: missing SourceURI" {
		t.Fatalf("ResolveInstalledPack error = %q", got)
	}
}

func TestRegisterRuntimeMetadataForPacksDoesNotSeedEmbeddedCRMFacts(t *testing.T) {
	previous := fieldprovenance.Default()
	defer fieldprovenance.SetDefault(previous)

	if err := RegisterRuntimeMetadataForPacks(context.Background(), nil, nil); err != nil {
		t.Fatalf("register L0-only runtime metadata: %v", err)
	}
	if isCompound, members := fieldprovenance.ClassifyCompound("Contact", "Name"); isCompound || len(members) != 0 {
		t.Fatalf("L0-only runtime metadata exposed Contact.Name compound = %v %#v, want no CRM declaration", isCompound, members)
	}
}

func TestRegisterRuntimeMetadataForPacksUsesSourceBackedCRMPayload(t *testing.T) {
	registerCRMHostPrimitivesForTest(t)
	previous := fieldprovenance.Default()
	defer fieldprovenance.SetDefault(previous)

	opts := officialCRMPackSourceOptionsForTest(t)
	source, diagnostics := ResolvePackSourceWithOptions("official_repo:salesforce-crm-standard", opts)
	if len(diagnostics) > 0 {
		t.Fatalf("resolve CRM source diagnostics: %v", diagnostics)
	}
	pack, err := NewSourceBackedPack(source, officialCRMSourceBackedPackOptionsForTest(t, opts)...)
	if err != nil {
		t.Fatalf("construct CRM source-backed pack: %v", err)
	}

	if err := RegisterRuntimeMetadataForPacks(context.Background(), nil, []Pack{pack}); err != nil {
		t.Fatalf("register source-backed CRM runtime metadata: %v", err)
	}
	isCompound, members := fieldprovenance.ClassifyCompound("Contact", "Name")
	if !isCompound {
		t.Fatal("source-backed CRM runtime metadata did not expose Contact.Name compound")
	}
	if want := []string{"Salutation", "FirstName", "LastName"}; !equalPackStringSlices(members, want) {
		t.Fatalf("Contact.Name members = %#v, want %#v", members, want)
	}
}

// TestRegisterRuntimeMetadataForPacksLastActivityDateProvenance verifies that
// when the CRM standard pack payload is fed to the fieldprovenance classifier,
// Account.LastActivityDate and Contact.LastActivityDate are classified as
// FieldSourceStandardSpec (pack-declared), while Case.LastActivityDate — which
// is NOT in the pack source — falls through to FieldSourceDiscovered. This is
// the production-path declaration-proof: the pack payload is the single
// authoritative surface, and no backend implicit injection is needed.
//
// This test builds the classifier directly from the CRM pack payload bytes
// rather than going through RegisterRuntimeMetadataForPacks, to avoid
// search-scope-order validation failures that are orthogonal to the
// LastActivityDate declaration contract.
func TestRegisterRuntimeMetadataForPacksLastActivityDateProvenance(t *testing.T) {
	previous := fieldprovenance.Default()
	defer fieldprovenance.SetDefault(previous)

	// Read the CRM pack object spec directly from the sibling checkout,
	// bypassing the full pack resolution and search-scope validation which
	// are orthogonal to the LastActivityDate declaration contract.
	crmPayload, err := os.ReadFile(filepath.Join(officialPackRootFromThisFile(t), "salesforce-crm-standard", "objects", "standard_objects_spec.json"))
	if err != nil {
		t.Fatalf("read CRM pack object spec: %v", err)
	}

	systemSpec, err := packspec.StandardObjectsSpecByCategoryJSON("system")
	if err != nil {
		t.Fatalf("load L0 system object spec: %v", err)
	}
	if err := fieldProvenanceInitializer.InitFieldProvenanceFromStandardObjectSpecs(
		fieldProvenanceInitializer.DefaultPlatformStandardFields(),
		systemSpec,
		crmPayload,
	); err != nil {
		t.Fatalf("initialize field provenance from CRM pack payload: %v", err)
	}

	for _, tc := range []struct {
		object string
		expect string
	}{
		{object: "Account", expect: constants.FieldSourceStandardSpec},
		{object: "Contact", expect: constants.FieldSourceStandardSpec},
		// Case is in the standard spec but does NOT declare LastActivityDate;
		// the classifier must return discovered (rule 7), confirming the pack
		// source is the single declaration surface.
		{object: "Case", expect: constants.FieldSourceDiscovered},
	} {
		t.Run(tc.object+".LastActivityDate", func(t *testing.T) {
			got := fieldprovenance.ClassifySource(tc.object, "LastActivityDate")
			if got != tc.expect {
				t.Fatalf("ClassifySource(%q, %q) = %q; want %q", tc.object, "LastActivityDate", got, tc.expect)
			}
		})
	}
}

func TestRegisterRuntimeMetadataForPacksPublishesSearchAllDefaultFields(t *testing.T) {
	searchmetadata.ResetPackSearchAllDefaultFieldsForTest()
	t.Cleanup(searchmetadata.ResetPackSearchAllDefaultFieldsForTest)
	metadata.ResetPackSearchScopeOrdersForTest()
	defer metadata.ResetPackSearchScopeOrdersForTest()
	metadata.ResetTenantRelatedListRulesForTest()
	defer metadata.ResetTenantRelatedListRulesForTest()

	source := &ResolvedPackSource{
		Kind: PackSourceKindLocalPath,
		URI:  "/tmp/search-default-fields-pack",
		Manifest: &PackManifest{
			ManifestFormatVersion: SupportedManifestFormatVersion,
			Name:                  "search-default-fields-pack",
			DisplayName:           "Search Default Fields Pack",
			Category:              AppPack,
			Version:               "0.1.0",
			PlatformCompatibility: PlatformCompatibility{Min: "0.1.0", MaxExclusive: "0.2.0"},
			Components: []ManifestComponent{
				{
					Kind:          ComponentKindObjectSpec,
					BusinessKey:   "issue",
					Manageability: ManageabilityLocked,
					Payload: []byte(testObjectSpecPayloadWithFields("Issue",
						`{"apiName":"Subject","label":"Subject","type":"string","required":true,"isNameField":true}`,
						`{"apiName":"ReporterEmail","label":"Reporter Email","type":"email"}`,
					)),
				},
				{
					Kind:          ComponentKindSearchAllDefaultFields,
					BusinessKey:   "issue-search-defaults",
					Manageability: ManageabilityLocked,
					Payload:       []byte(`[{"objectApiName":"Issue","fields":["Id","Subject","ReporterEmail"]}]`),
					DependsOn:     []ComponentReference{{Kind: ComponentKindObjectSpec, BusinessKey: "issue"}},
				},
			},
		},
	}

	pack, err := NewSourceBackedPack(source)
	if err != nil {
		t.Fatalf("NewSourceBackedPack: %v", err)
	}
	decl := buildRuntimeDeclaration(pack)
	specs, err := ParseObjectSpecsPayloads(decl.objectSpecPayloads, "search-default-fields-runtime")
	if err != nil {
		t.Fatalf("ParseObjectSpecsPayloads: %v", err)
	}
	if len(specs) != 1 {
		t.Fatalf("runtime object payload count = %d, want 1", len(specs))
	}
	if len(specs[0].SearchAllDefaultFields) != 0 {
		t.Fatalf("runtime object payload searchAllDefaultFields = %#v, want dedicated search_all_default_fields runtime surface instead of object_spec overlay", specs[0].SearchAllDefaultFields)
	}

	if err := RegisterRuntimeMetadataForPacks(context.Background(), nil, []Pack{pack}); err != nil {
		t.Fatalf("RegisterRuntimeMetadataForPacks: %v", err)
	}
	got, ok := searchmetadata.PackSearchAllDefaultFieldsForObject("issue")
	if !ok {
		t.Fatal("PackSearchAllDefaultFieldsForObject(issue) not registered")
	}
	want := []string{"Id", "Subject", "ReporterEmail"}
	if !equalPackStringSlices(got, want) {
		t.Fatalf("PackSearchAllDefaultFieldsForObject(issue) = %#v, want %#v", got, want)
	}
}

func TestRegisterRuntimeMetadataForPacksPublishesSearchScopeOrder(t *testing.T) {
	metadata.ResetPackSearchScopeOrdersForTest()
	defer metadata.ResetPackSearchScopeOrdersForTest()
	metadata.ResetTenantRelatedListRulesForTest()
	defer metadata.ResetTenantRelatedListRulesForTest()

	sourceDir := writeRuntimeSearchScopePackSource(t, "jira-like-sample")
	pack, err := ResolveInstalledSourceBackedPack(
		"jira-like-sample",
		AppPack,
		string(PackSourceKindLocalPath),
		sourceDir,
	)
	if err != nil {
		t.Fatalf("resolve source-backed pack: %v", err)
	}

	if err := RegisterRuntimeMetadataForPacks(context.Background(), nil, []Pack{pack}); err != nil {
		t.Fatalf("register runtime metadata: %v", err)
	}

	got := metadata.PackSearchScopeObjectNames()
	want := []string{"Project", "Issue"}
	if !equalPackStringSlices(got, want) {
		t.Fatalf("PackSearchScopeObjectNames = %#v, want %#v", got, want)
	}
}

func TestRegisterRuntimeMetadataForPacksPublishesBuilderDeclarationSurface(t *testing.T) {
	metadata.ResetPackSearchScopeOrdersForTest()
	defer metadata.ResetPackSearchScopeOrdersForTest()
	metadata.ResetTenantRelatedListRulesForTest()
	defer metadata.ResetTenantRelatedListRulesForTest()

	source := &ResolvedPackSource{
		Kind: PackSourceKindLocalPath,
		URI:  "/tmp/runtime-declaration-publish-pack",
		Manifest: &PackManifest{
			ManifestFormatVersion: SupportedManifestFormatVersion,
			Name:                  "runtime-declaration-publish-pack",
			DisplayName:           "Runtime Declaration Publish Pack",
			Category:              AppPack,
			Version:               "0.1.0",
			PlatformCompatibility: PlatformCompatibility{Min: "0.1.0", MaxExclusive: "0.2.0"},
			Components: []ManifestComponent{
				{
					Kind:          ComponentKindObjectSpec,
					BusinessKey:   "issue",
					Manageability: ManageabilityLocked,
					Payload: []byte(testObjectSpecPayloadWithFields("Issue",
						`{"apiName": "Subject", "label": "Subject", "type": "string", "required": true, "isNameField": true}`,
						`{"apiName": "FileLinks__c", "label": "File Links", "type": "string"}`,
					)),
				},
				{
					Kind:          ComponentKindRelatedListRule,
					BusinessKey:   "Issue/Comment__c",
					Manageability: ManageabilityDefaultsOnly,
					Payload: []byte(`{
						"rules": [{
							"parentObjectApiName": "Issue",
							"childObjectApiName": "Comment__c",
							"lookupField": "IssueId__c",
							"bucket": "recommended",
							"fields": ["Subject"]
						}]
					}`),
				},
				{
					Kind:          ComponentKindSearchScopeOrder,
					BusinessKey:   "issue-search-order",
					Manageability: ManageabilityDefaultsOnly,
					Payload:       []byte(`[{"name":"Issue","label":"Issue"}]`),
				},
			},
		},
	}

	pack, err := NewSourceBackedPack(source)
	if err != nil {
		t.Fatalf("NewSourceBackedPack: %v", err)
	}
	decl := buildRuntimeDeclaration(pack)

	if err := RegisterRuntimeMetadataForPacks(context.Background(), nil, []Pack{pack}); err != nil {
		t.Fatalf("RegisterRuntimeMetadataForPacks: %v", err)
	}

	gotSearch := metadata.PackSearchScopeObjectNames()
	wantSearch := make([]string, 0, len(decl.searchScopeOrder))
	for _, item := range decl.searchScopeOrder {
		wantSearch = append(wantSearch, item.Name)
	}
	if !equalPackStringSlices(gotSearch, wantSearch) {
		t.Fatalf("PackSearchScopeObjectNames = %#v, want builder projection %#v", gotSearch, wantSearch)
	}

	resolved := metadata.ResolveRelatedListCandidates("Issue", []metadata.RelatedListCandidate{{
		ObjectAPIName: "Comment__c",
		LookupField:   "IssueId__c",
		Label:         "Comments",
	}})
	if len(resolved) != 1 {
		t.Fatalf("ResolveRelatedListCandidates len = %d, want 1", len(resolved))
	}
	if !resolved[0].IsRecommended || resolved[0].Bucket != string(metadata.BucketRecommended) {
		t.Fatalf("resolved related-list candidate = %+v, want recommended builder projection", resolved[0])
	}
	if !reflect.DeepEqual(resolved[0].Fields, decl.relatedListRules[0].Fields) {
		t.Fatalf("resolved related-list fields = %#v, want builder projection %#v", resolved[0].Fields, decl.relatedListRules[0].Fields)
	}
}

func TestRegisterPersistedInstalledRuntimeMetadataPublishesHrefRewriteFields(t *testing.T) {
	files.ResetHrefFieldsForTest()
	t.Cleanup(files.ResetHrefFieldsForTest)

	source := &ResolvedPackSource{
		Kind: PackSourceKindLocalPath,
		URI:  "/tmp/runtime-href-fields-pack",
		Manifest: &PackManifest{
			ManifestFormatVersion: SupportedManifestFormatVersion,
			Name:                  "runtime-href-fields-pack",
			DisplayName:           "Runtime Href Fields Pack",
			Category:              AppPack,
			Version:               "0.1.0",
			PlatformCompatibility: PlatformCompatibility{Min: "0.1.0", MaxExclusive: "0.2.0"},
			Components: []ManifestComponent{
				{
					Kind:          ComponentKindObjectSpec,
					BusinessKey:   "issue",
					Manageability: ManageabilityLocked,
					Payload: []byte(testObjectSpecPayloadWithFields("Issue",
						`{"apiName": "Subject", "label": "Subject", "type": "string", "required": true, "isNameField": true}`,
						`{"apiName": "FileLinks__c", "label": "File Links", "type": "string"}`,
					)),
				},
				{
					Kind:          ComponentKindHrefRewriteFields,
					BusinessKey:   "issue-file-hrefs",
					Manageability: ManageabilityLocked,
					Payload:       []byte(`{"fields":[{"parentApiName":"Issue","fieldApiName":"FileLinks__c"}]}`),
					DependsOn:     []ComponentReference{{Kind: ComponentKindObjectSpec, BusinessKey: "issue"}},
				},
			},
		},
	}
	pack, err := NewSourceBackedPack(source)
	if err != nil {
		t.Fatalf("NewSourceBackedPack: %v", err)
	}
	decl, err := BuildPersistedRuntimeDeclaration(pack)
	if err != nil {
		t.Fatalf("BuildPersistedRuntimeDeclaration: %v", err)
	}

	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	expectInstalledPackRuntimeDeclarationColumnsWithStartupShallow(mock, true, true)
	mock.ExpectQuery("SELECT Name, Category, AppliedPayloadHash, ManifestFormatVersion, SourceKind, SourceURI, SourceRevision,\\s+RuntimeObjectSpecDeclarations, RuntimeRelatedListRules, RuntimeSearchScopeOrder").
		WillReturnRows(sqlmock.NewRows([]string{
			"Name", "Category", "AppliedPayloadHash", "ManifestFormatVersion", "SourceKind", "SourceURI", "SourceRevision",
			"RuntimeObjectSpecDeclarations", "RuntimeRelatedListRules", "RuntimeSearchScopeOrder", "RuntimeSearchAllDefaultFields", "RuntimeStartupShallowDeclarations",
		}).AddRow(
			pack.Name(),
			string(AppPack),
			"hash",
			SupportedManifestFormatVersion,
			string(PackSourceKindLocalPath),
			"/tmp/runtime-href-fields-pack",
			"rev",
			decl.ObjectPayloadsJSON(),
			decl.RelatedListRulesJSON(),
			decl.SearchScopeOrderJSON(),
			decl.SearchAllDefaultFieldsJSON(),
			decl.StartupShallowDeclaredRowsJSON(),
		))

	if err := RegisterPersistedInstalledRuntimeMetadata(context.Background(), db); err != nil {
		t.Fatalf("RegisterPersistedInstalledRuntimeMetadata: %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
	if got := files.FieldsToRewriteForObject("Issue"); !reflect.DeepEqual(got, []string{"FileLinks__c"}) {
		t.Fatalf("FieldsToRewriteForObject(Issue) = %#v, want [FileLinks__c]", got)
	}
}

func TestRegisterRuntimeMetadataForPacksRejectsConflictingSearchScopeOrdersBeforePublishing(t *testing.T) {
	metadata.ResetPackSearchScopeOrdersForTest()
	defer metadata.ResetPackSearchScopeOrdersForTest()

	packA := &runtimeSearchScopeValidationPack{
		name:         "a-pack",
		payloads:     [][]byte{[]byte(`[{"apiName":"Account","label":"Account","tableType":"standard_object","category":"jira","sharingModel":"Private","enableHierarchySharing":false,"fields":[{"apiName":"Name","label":"Name","type":"string","required":true,"isNameField":true}]}]`)},
		searchScope:  []metadata.SearchScopeOrderItem{{Name: "Account"}},
		ownedObjects: []string{"Account"},
	}
	packB := &runtimeSearchScopeValidationPack{
		name:         "b-pack",
		payloads:     [][]byte{[]byte(`[{"apiName":"Account","label":"Account","tableType":"standard_object","category":"jira","sharingModel":"Private","enableHierarchySharing":false,"fields":[{"apiName":"Name","label":"Name","type":"string","required":true,"isNameField":true}]}]`)},
		searchScope:  []metadata.SearchScopeOrderItem{{Name: "Account"}},
		ownedObjects: []string{"Account"},
	}

	err := RegisterRuntimeMetadataForPacks(context.Background(), nil, []Pack{packA, packB})
	if err == nil || !strings.Contains(err.Error(), "search scope object Account declared by both a-pack and b-pack") {
		t.Fatalf("RegisterRuntimeMetadataForPacks error = %v, want conflicting search-scope-order failure", err)
	}
	if got := metadata.PackSearchScopeObjectNames(); len(got) != 0 {
		t.Fatalf("PackSearchScopeObjectNames = %#v, want empty after failed registration", got)
	}
}

func TestRegisterRuntimeMetadataForPacksRejectsHrefRewriteFieldNotDeclaredByObjectSpec(t *testing.T) {
	files.ResetHrefFieldsForTest()
	defer files.ResetHrefFieldsForTest()

	pack := &runtimeHrefRewriteValidationPack{
		name: "object-owner",
		payloads: [][]byte{[]byte(`[
			{"apiName":"Issue","label":"Issue","tableType":"standard_object","category":"jira","sharingModel":"Private","enableHierarchySharing":false,"fields":[{"apiName":"Subject","label":"Subject","type":"string","required":true,"isNameField":true}]}
		]`)},
		hrefFields: []files.HrefRewriteField{{ParentApiName: "Issue", FieldApiName: "FileLinks__c"}},
	}

	err := RegisterRuntimeMetadataForPacks(context.Background(), nil, []Pack{pack})
	if err == nil {
		t.Fatal("RegisterRuntimeMetadataForPacks accepted href_rewrite_fields for missing field, want rejection")
	}
	if got := err.Error(); !strings.Contains(got, "href_rewrite_fields") || !strings.Contains(got, "Issue/FileLinks__c") {
		t.Fatalf("RegisterRuntimeMetadataForPacks error = %v, want missing field rejection for Issue/FileLinks__c", got)
	}
	if got := files.FieldsToRewriteForObject("Issue"); len(got) != 0 {
		t.Fatalf("FieldsToRewriteForObject(Issue) = %#v, want empty after failed registration", got)
	}
}

func TestRegisterL0RuntimeMetadataClearsHrefRewriteFields(t *testing.T) {
	files.ResetHrefFieldsForTest()
	t.Cleanup(files.ResetHrefFieldsForTest)

	files.RegisterHrefFields(files.HrefRewriteField{ParentApiName: "Issue", FieldApiName: "FileLinks__c"})
	if got := files.FieldsToRewriteForObject("Issue"); len(got) != 1 {
		t.Fatalf("precondition FieldsToRewriteForObject(Issue) = %#v, want one field", got)
	}

	if err := RegisterL0RuntimeMetadata(context.Background(), nil); err != nil {
		t.Fatalf("RegisterL0RuntimeMetadata: %v", err)
	}
	if got := files.FieldsToRewriteForObject("Issue"); len(got) != 0 {
		t.Fatalf("FieldsToRewriteForObject(Issue) = %#v, want empty after L0 registration", got)
	}
}

func TestRegisterRuntimeMetadataForPacksRejectsInvalidDeclarationsBeforePublishing(t *testing.T) {
	pack := &runtimeMetadataValidationPack{
		name: "bad-pack",
		payloads: [][]byte{[]byte(`[
			{"apiName":"Issue","keyPrefix":"500","category":"jira"},
			{"apiName":"Bug","keyPrefix":"500","category":"jira"}
		]`)},
	}

	err := RegisterRuntimeMetadataForPacks(context.Background(), nil, []Pack{pack})
	if err == nil || !strings.Contains(err.Error(), "conflicting object identity key prefix 500") {
		t.Fatalf("RegisterRuntimeMetadataForPacks error = %v, want duplicate key prefix validation failure", err)
	}
}

type runtimeMetadataValidationPack struct {
	name     string
	payloads [][]byte
}

func (p *runtimeMetadataValidationPack) Name() string { return p.name }

func (p *runtimeMetadataValidationPack) Category() Category { return AppPack }

func (p *runtimeMetadataValidationPack) Apply(context.Context, ApplyDeps) error { return nil }

func (p *runtimeMetadataValidationPack) Verify(context.Context, VerifyDeps) error { return nil }

func (p *runtimeMetadataValidationPack) RuntimeObjectSpecPayloads() [][]byte {
	return p.payloads
}

type runtimeSearchScopeValidationPack struct {
	name         string
	payloads     [][]byte
	searchScope  []metadata.SearchScopeOrderItem
	ownedObjects []string
}

func (p *runtimeSearchScopeValidationPack) Name() string { return p.name }

func (p *runtimeSearchScopeValidationPack) Category() Category { return AppPack }

func (p *runtimeSearchScopeValidationPack) Apply(context.Context, ApplyDeps) error { return nil }

func (p *runtimeSearchScopeValidationPack) Verify(context.Context, VerifyDeps) error { return nil }

func (p *runtimeSearchScopeValidationPack) RuntimeObjectSpecPayloads() [][]byte {
	return p.payloads
}

func (p *runtimeSearchScopeValidationPack) RuntimeSearchScopeOrder() []metadata.SearchScopeOrderItem {
	return append([]metadata.SearchScopeOrderItem(nil), p.searchScope...)
}

func (p *runtimeSearchScopeValidationPack) OwnedObjectAPINames() []string {
	return append([]string(nil), p.ownedObjects...)
}

type runtimeHrefRewriteValidationPack struct {
	name       string
	payloads   [][]byte
	hrefFields []files.HrefRewriteField
}

func (p *runtimeHrefRewriteValidationPack) Name() string { return p.name }

func (p *runtimeHrefRewriteValidationPack) Category() Category { return AppPack }

func (p *runtimeHrefRewriteValidationPack) Apply(context.Context, ApplyDeps) error { return nil }

func (p *runtimeHrefRewriteValidationPack) Verify(context.Context, VerifyDeps) error { return nil }

func (p *runtimeHrefRewriteValidationPack) RuntimeObjectSpecPayloads() [][]byte {
	return p.payloads
}

func (p *runtimeHrefRewriteValidationPack) RuntimeHrefRewriteFields() []files.HrefRewriteField {
	return append([]files.HrefRewriteField(nil), p.hrefFields...)
}

func writeRuntimeTestPackSource(t *testing.T, name string) string {
	t.Helper()

	root := t.TempDir()
	objectsDir := filepath.Join(root, "objects")
	if err := os.MkdirAll(objectsDir, 0o755); err != nil {
		t.Fatalf("mkdir objects dir: %v", err)
	}

	manifest := `{
  "manifestFormatVersion": "1",
  "name": "` + name + `",
  "displayName": "Jira-like Sample",
  "category": "app-pack",
  "version": "0.1.0",
  "platformCompatibility": {
    "min": "0.2.0",
    "maxExclusive": "0.3.0"
  },
  "dependencies": [],
  "components": [
    {
      "kind": "object_spec",
      "businessKey": "issue",
      "manageability": "locked",
      "path": "objects/issue.json"
    }
  ]
}`
	if err := os.WriteFile(filepath.Join(root, "pack.json"), []byte(manifest), 0o644); err != nil {
		t.Fatalf("write pack manifest: %v", err)
	}

	objectSpec := `[
  {
    "apiName": "Issue",
    "label": "Issue",
    "pluralLabel": "Issues",
    "tableType": "standard_object",
    "category": "jira",
    "sharingModel": "Private",
    "enableHierarchySharing": false,
    "description": "Issue work item",
    "fields": [
      {
        "apiName": "Subject",
        "label": "Subject",
        "type": "string",
        "required": true,
        "isNameField": true
      }
    ]
  }
]`
	if err := os.WriteFile(filepath.Join(objectsDir, "issue.json"), []byte(objectSpec), 0o644); err != nil {
		t.Fatalf("write object spec: %v", err)
	}

	return root
}

func writeRuntimeValidationRulePackSource(t *testing.T, name string) string {
	t.Helper()

	root := t.TempDir()
	objectsDir := filepath.Join(root, "objects")
	relatedDir := filepath.Join(root, "related-list-rules")
	searchDir := filepath.Join(root, "search")
	rulesDir := filepath.Join(root, "validation-rules")
	if err := os.MkdirAll(objectsDir, 0o755); err != nil {
		t.Fatalf("mkdir objects dir: %v", err)
	}
	if err := os.MkdirAll(relatedDir, 0o755); err != nil {
		t.Fatalf("mkdir related-list rules dir: %v", err)
	}
	if err := os.MkdirAll(searchDir, 0o755); err != nil {
		t.Fatalf("mkdir search dir: %v", err)
	}
	if err := os.MkdirAll(rulesDir, 0o755); err != nil {
		t.Fatalf("mkdir validation rules dir: %v", err)
	}

	manifest := `{
  "manifestFormatVersion": "1",
  "name": "` + name + `",
  "displayName": "Jira-like Sample",
  "category": "app-pack",
  "version": "0.1.0",
  "platformCompatibility": {
    "min": "0.2.0",
    "maxExclusive": "0.3.0"
  },
  "dependencies": [],
  "components": [
    {
      "kind": "object_spec",
      "businessKey": "issue",
      "manageability": "locked",
      "path": "objects/issue.json"
    },
    {
      "kind": "related_list_rule",
      "businessKey": "issue-comments",
      "manageability": "defaults_only",
      "path": "related-list-rules/issue-comments.json"
    },
    {
      "kind": "search_scope_order",
      "businessKey": "default",
      "manageability": "defaults_only",
      "path": "search/scope-order.json"
    },
    {
      "kind": "validation_rule",
      "businessKey": "issue-required",
      "manageability": "defaults_only",
      "path": "validation-rules/issue-required.json"
    }
  ]
}`
	if err := os.WriteFile(filepath.Join(root, "pack.json"), []byte(manifest), 0o644); err != nil {
		t.Fatalf("write pack manifest: %v", err)
	}

	objectSpec := `[
  {
    "apiName": "Issue",
    "label": "Issue",
    "pluralLabel": "Issues",
    "tableType": "standard_object",
    "category": "jira",
    "sharingModel": "Private",
    "enableHierarchySharing": false,
    "description": "Issue work item",
    "fields": [
      {
        "apiName": "Subject",
        "label": "Subject",
        "type": "string",
        "required": true,
        "isNameField": true
      }
    ]
  }
]`
	if err := os.WriteFile(filepath.Join(objectsDir, "issue.json"), []byte(objectSpec), 0o644); err != nil {
		t.Fatalf("write object spec: %v", err)
	}

	relatedListRules := `{
  "rules": [
    {
      "parentObjectApiName": "Issue",
      "childObjectApiName": "Comment__c",
      "lookupField": "IssueId__c",
      "bucket": "recommended",
      "fields": ["Subject"]
    }
  ]
}`
	if err := os.WriteFile(filepath.Join(relatedDir, "issue-comments.json"), []byte(relatedListRules), 0o644); err != nil {
		t.Fatalf("write related-list rule: %v", err)
	}

	scopeOrder := `[
  {"name": "Issue", "label": "Issues"}
]`
	if err := os.WriteFile(filepath.Join(searchDir, "scope-order.json"), []byte(scopeOrder), 0o644); err != nil {
		t.Fatalf("write search scope order: %v", err)
	}

	validationRules := `{
  "validationRules": [
    {
      "id": "vrule_issue_subject_required",
      "objectApiName": "Issue",
      "name": "Issue_Subject_Required",
      "active": true,
      "condition": "ISBLANK(Subject)",
      "errorMessage": "Subject is required"
    }
  ]
}`
	if err := os.WriteFile(filepath.Join(rulesDir, "issue-required.json"), []byte(validationRules), 0o644); err != nil {
		t.Fatalf("write validation rule: %v", err)
	}

	return root
}

func writeRuntimeListFieldsPackSource(t *testing.T, name string) string {
	t.Helper()

	root := t.TempDir()
	objectsDir := filepath.Join(root, "objects")
	if err := os.MkdirAll(objectsDir, 0o755); err != nil {
		t.Fatalf("mkdir objects dir: %v", err)
	}

	manifest := `{
  "manifestFormatVersion": "1",
  "name": "` + name + `",
  "displayName": "Jira-like Sample",
  "category": "app-pack",
  "version": "0.1.0",
  "platformCompatibility": {
    "min": "0.2.0",
    "maxExclusive": "0.3.0"
  },
  "dependencies": [],
  "components": [
    {
      "kind": "object_spec",
      "businessKey": "issue",
      "manageability": "locked",
      "path": "objects/issue.json"
    }
  ]
}`
	if err := os.WriteFile(filepath.Join(root, "pack.json"), []byte(manifest), 0o644); err != nil {
		t.Fatalf("write pack manifest: %v", err)
	}
	if err := os.WriteFile(filepath.Join(objectsDir, "issue.json"), []byte(testObjectSpecPayloadWithListFields("Issue", []string{"Subject", "Status"})), 0o644); err != nil {
		t.Fatalf("write object spec: %v", err)
	}
	return root
}

func writeRuntimeSearchScopePackSource(t *testing.T, name string) string {
	t.Helper()

	root := t.TempDir()
	objectsDir := filepath.Join(root, "objects")
	searchDir := filepath.Join(root, "search")
	if err := os.MkdirAll(objectsDir, 0o755); err != nil {
		t.Fatalf("mkdir objects dir: %v", err)
	}
	if err := os.MkdirAll(searchDir, 0o755); err != nil {
		t.Fatalf("mkdir search dir: %v", err)
	}

	manifest := `{
  "manifestFormatVersion": "1",
  "name": "` + name + `",
  "displayName": "Jira-like Sample",
  "category": "app-pack",
  "version": "0.1.0",
  "platformCompatibility": {
    "min": "0.2.0",
    "maxExclusive": "0.3.0"
  },
  "dependencies": [],
  "components": [
    {
      "kind": "object_spec",
      "businessKey": "issue",
      "manageability": "locked",
      "path": "objects/issue.json"
    },
    {
      "kind": "search_scope_order",
      "businessKey": "default",
      "manageability": "defaults_only",
      "path": "search/scope-order.json"
    }
  ]
}`
	if err := os.WriteFile(filepath.Join(root, "pack.json"), []byte(manifest), 0o644); err != nil {
		t.Fatalf("write pack manifest: %v", err)
	}

	objectSpec := `[
  {
    "apiName": "Issue",
    "label": "Issue",
    "pluralLabel": "Issues",
    "tableType": "standard_object",
    "category": "jira",
    "sharingModel": "Private",
    "enableHierarchySharing": false,
    "description": "Issue work item",
    "searchable": true,
    "fields": [
      {
        "apiName": "Subject",
        "label": "Subject",
        "type": "string",
        "required": true,
        "isNameField": true
      }
    ]
  },
  {
    "apiName": "Project",
    "label": "Project",
    "pluralLabel": "Projects",
    "tableType": "standard_object",
    "category": "jira",
    "sharingModel": "Private",
    "enableHierarchySharing": false,
    "description": "Project work item",
    "searchable": true,
    "fields": [
      {
        "apiName": "Name",
        "label": "Name",
        "type": "string",
        "required": true,
        "isNameField": true
      }
    ]
  }
]`
	if err := os.WriteFile(filepath.Join(objectsDir, "issue.json"), []byte(objectSpec), 0o644); err != nil {
		t.Fatalf("write object spec: %v", err)
	}

	scopeOrder := `[
  {"name": "Project", "label": "Projects"},
  {"name": "Issue", "label": "Issues"}
]`
	if err := os.WriteFile(filepath.Join(searchDir, "scope-order.json"), []byte(scopeOrder), 0o644); err != nil {
		t.Fatalf("write search scope order: %v", err)
	}

	return root
}

func equalPackStringSlices(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}
