package bootstrapimpl

import (
	"regexp"
	"strings"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
	domainschema "github.com/nexuscrm/kernel/pkg/schema"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

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

	query := regexp.QuoteMeta("SELECT `ApiName`, `SharingModel`, COALESCE(`EnableHierarchySharing`, 0), COALESCE(`IsCustomized`, 0), `AccessPolicy`, `AccessPolicyRecordMutationPackName`, `AccessPolicyLifecyclePackName` FROM `_System_Object` WHERE `TableType` = ? AND COALESCE(`IsDeleted`, 0) = 0")
	mock.ExpectQuery(query).
		WithArgs(string(constants.TableTypeStandardObject)).
		WillReturnRows(sqlmock.NewRows([]string{
			"ApiName",
			"SharingModel",
			"EnableHierarchySharing",
			"IsCustomized",
			"AccessPolicy",
			"AccessPolicyRecordMutationPackName",
			"AccessPolicyLifecyclePackName",
		}).AddRow(
			"RecordType",
			string(constants.SharingModelPublicReadWrite),
			false,
			false,
			`{"recordMutation":{"deny":[{"surfaces":["singleCreate"],"message":"closed"}]}}`,
			"salesforce-crm-standard",
			"",
		))

	result := &AssertionResult{}
	AssertManagedAccessPolicyConsistency(db, result, map[string]domainschema.ManagedStandardObjectPolicy{
		"recordtype": {
			SharingModel:           string(constants.SharingModelPublicReadWrite),
			EnableHierarchySharing: false,
			AccessPolicy:           &models.ObjectAccessPolicy{},
		},
	})
	if len(result.Violations) != 0 {
		t.Fatalf("violations = %#v, want none when only pack-owned recordMutation differs", result.Violations)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	query := regexp.QuoteMeta("SELECT `ApiName`, `SharingModel`, COALESCE(`EnableHierarchySharing`, 0), COALESCE(`IsCustomized`, 0), `AccessPolicy`, `AccessPolicyRecordMutationPackName`, `AccessPolicyLifecyclePackName` FROM `_System_Object` WHERE `TableType` = ? AND COALESCE(`IsDeleted`, 0) = 0")
	mock.ExpectQuery(query).
		WithArgs(string(constants.TableTypeStandardObject)).
		WillReturnRows(sqlmock.NewRows([]string{
			"ApiName",
			"SharingModel",
			"EnableHierarchySharing",
			"IsCustomized",
			"AccessPolicy",
			"AccessPolicyRecordMutationPackName",
			"AccessPolicyLifecyclePackName",
		}).AddRow(
			"RecordType",
			string(constants.SharingModelPublicReadWrite),
			false,
			false,
			`{"recordMutation":{"deny":[{"surfaces":["singleCreate"],"message":"closed"}]}}`,
			"",
			"",
		))

	result := &AssertionResult{}
	AssertManagedAccessPolicyConsistency(db, result, map[string]domainschema.ManagedStandardObjectPolicy{
		"recordtype": {
			SharingModel:           string(constants.SharingModelPublicReadWrite),
			EnableHierarchySharing: false,
			AccessPolicy:           &models.ObjectAccessPolicy{},
		},
	})
	if len(result.Violations) == 0 {
		t.Fatal("violations = 0, want unowned recordMutation AccessPolicy drift")
	}
	if result.Violations[0].Category != "ManagedAccessPolicyDrift" {
		t.Fatalf("first violation = %#v, want ManagedAccessPolicyDrift", result.Violations[0])
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	query := regexp.QuoteMeta("SELECT `ApiName`, `SharingModel`, COALESCE(`EnableHierarchySharing`, 0), COALESCE(`IsCustomized`, 0), `AccessPolicy`, `AccessPolicyRecordMutationPackName`, `AccessPolicyLifecyclePackName` FROM `_System_Object` WHERE `TableType` = ? AND COALESCE(`IsDeleted`, 0) = 0")
	mock.ExpectQuery(query).
		WithArgs(string(constants.TableTypeStandardObject)).
		WillReturnRows(sqlmock.NewRows([]string{
			"ApiName",
			"SharingModel",
			"EnableHierarchySharing",
			"IsCustomized",
			"AccessPolicy",
			"AccessPolicyRecordMutationPackName",
			"AccessPolicyLifecyclePackName",
		}).AddRow(
			"ProcessDefinition",
			string(constants.SharingModelPublicReadWrite),
			false,
			false,
			`{"lifecycle":{"hooks":[{"phase":"after_create_in_tx","capability":"crm.approval-process-child-sync"}]}}`,
			"",
			"salesforce-crm-standard",
		))

	result := &AssertionResult{}
	AssertManagedAccessPolicyConsistency(db, result, map[string]domainschema.ManagedStandardObjectPolicy{
		"processdefinition": {
			SharingModel:           string(constants.SharingModelPublicReadWrite),
			EnableHierarchySharing: false,
			AccessPolicy:           &models.ObjectAccessPolicy{},
		},
	})
	if len(result.Violations) != 0 {
		t.Fatalf("violations = %#v, want none when only pack-owned lifecycle differs", result.Violations)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	query := regexp.QuoteMeta("SELECT `ApiName`, `SharingModel`, COALESCE(`EnableHierarchySharing`, 0), COALESCE(`IsCustomized`, 0), `AccessPolicy`, `AccessPolicyRecordMutationPackName`, `AccessPolicyLifecyclePackName` FROM `_System_Object` WHERE `TableType` = ? AND COALESCE(`IsDeleted`, 0) = 0")
	mock.ExpectQuery(query).
		WithArgs(string(constants.TableTypeStandardObject)).
		WillReturnRows(sqlmock.NewRows([]string{
			"ApiName",
			"SharingModel",
			"EnableHierarchySharing",
			"IsCustomized",
			"AccessPolicy",
			"AccessPolicyRecordMutationPackName",
			"AccessPolicyLifecyclePackName",
		}).AddRow(
			"ProcessDefinition",
			string(constants.SharingModelPublicReadWrite),
			false,
			false,
			`{"lifecycle":{"hooks":[{"phase":"after_create_in_tx","capability":"crm.approval-process-child-sync"}]}}`,
			"",
			"",
		))

	result := &AssertionResult{}
	AssertManagedAccessPolicyConsistency(db, result, map[string]domainschema.ManagedStandardObjectPolicy{
		"processdefinition": {
			SharingModel:           string(constants.SharingModelPublicReadWrite),
			EnableHierarchySharing: false,
			AccessPolicy:           &models.ObjectAccessPolicy{},
		},
	})
	if len(result.Violations) == 0 {
		t.Fatal("violations = 0, want unowned lifecycle AccessPolicy drift")
	}
	if result.Violations[0].Category != "ManagedAccessPolicyDrift" {
		t.Fatalf("first violation = %#v, want ManagedAccessPolicyDrift", result.Violations[0])
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestManagedTableAccessPoliciesCollectsGroupMemberWriteValidation(t *testing.T) {
	policies := managedTableAccessPolicies(GetManagedTableDefinitions())
	policy, ok := policies[strings.ToLower(constants.TableGroupMember)]
	if !ok {
		t.Fatalf("%s missing from managed table access policies", constants.TableGroupMember)
	}
	if policy == nil || policy.WriteValidation == nil {
		t.Fatalf("%s writeValidation policy missing: %+v", constants.TableGroupMember, policy)
	}

	const wantCapability = "platform.security.write.role-backed-group-member"
	want := map[string]string{
		"before_create": wantCapability,
		"before_update": wantCapability,
		"before_delete": wantCapability,
	}
	for _, validator := range policy.WriteValidation.Validators {
		capability, ok := want[validator.Phase]
		if !ok {
			t.Fatalf("%s writeValidation includes unexpected phase %q in %+v", constants.TableGroupMember, validator.Phase, policy.WriteValidation.Validators)
		}
		if validator.Capability != capability {
			t.Fatalf("%s writeValidation[%s] capability = %q, want %q", constants.TableGroupMember, validator.Phase, validator.Capability, capability)
		}
		delete(want, validator.Phase)
	}
	if len(want) != 0 {
		t.Fatalf("%s writeValidation missing phase(s): %+v", constants.TableGroupMember, want)
	}
}

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

	query := regexp.QuoteMeta("SELECT `ApiName`, `AccessPolicy`, `AccessPolicyRecordMutationPackName`, `AccessPolicyLifecyclePackName` FROM `_System_Object` WHERE COALESCE(`IsDeleted`, 0) = 0")
	mock.ExpectQuery(query).
		WillReturnRows(sqlmock.NewRows([]string{
			"ApiName",
			"AccessPolicy",
			"AccessPolicyRecordMutationPackName",
			"AccessPolicyLifecyclePackName",
		}).AddRow(
			"GroupMember",
			`{}`,
			"",
			"",
		))

	result := &AssertionResult{}
	AssertManagedTableAccessPolicyConsistency(db, result, map[string]*models.ObjectAccessPolicy{
		strings.ToLower(constants.TableGroupMember): {
			WriteValidation: &models.RecordWriteValidationAccessPolicy{
				Validators: []models.RecordWriteValidationPolicy{
					{Phase: "before_create", Capability: "platform.security.write.role-backed-group-member"},
				},
			},
		},
	})
	if len(result.Violations) == 0 {
		t.Fatal("violations = 0, want system_core AccessPolicy drift")
	}
	if result.Violations[0].Category != "ManagedAccessPolicyDrift" || result.Violations[0].Severity != constants.SeverityError {
		t.Fatalf("first violation = %#v, want managed access policy error drift", result.Violations[0])
	}
	if !strings.Contains(result.Violations[0].Description, "writeValidation") {
		t.Fatalf("violation description = %q, want writeValidation drift details", result.Violations[0].Description)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	query := regexp.QuoteMeta("SELECT `ApiName`, `AccessPolicy` FROM `_System_Object` WHERE COALESCE(`IsDeleted`, 0) = 0")
	// Persisted AccessPolicy carries writeValidation but NOT ownerResolution — the
	// stale-schema shape an upgraded org has before setup apply reconciles the subfact.
	mock.ExpectQuery(query).
		WillReturnRows(sqlmock.NewRows([]string{"ApiName", "AccessPolicy"}).AddRow(
			"PermissionSet",
			`{"writeValidation":{"validators":[{"phase":"before_create","capability":"platform.security.write.system-profile-owned-immutable"}]}}`,
		))

	result := &AssertionResult{}
	AssertManagedOwnerResolutionReconciled(db, result, map[string]struct{}{"permissionset": {}})
	if len(result.Violations) != 1 {
		t.Fatalf("violations = %d, want 1: %+v", len(result.Violations), result.Violations)
	}
	v := result.Violations[0]
	if v.Category != "ManagedOwnerResolutionReconciliation" || v.Severity != constants.SeverityError {
		t.Fatalf("violation = %#v, want managed owner-resolution reconciliation error", v)
	}
	if v.Object != "permissionset" {
		t.Fatalf("object = %q, want permissionset", v.Object)
	}
	if !strings.Contains(v.Description, "ownerResolution") || !strings.Contains(v.Description, "setup apply") {
		t.Fatalf("description = %q, want ownerResolution drift + setup apply guidance", v.Description)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	query := regexp.QuoteMeta("SELECT `ApiName`, `AccessPolicy` FROM `_System_Object` WHERE COALESCE(`IsDeleted`, 0) = 0")
	// Persisted AccessPolicy carries ownerResolution — the reconciled shape after setup apply.
	mock.ExpectQuery(query).
		WillReturnRows(sqlmock.NewRows([]string{"ApiName", "AccessPolicy"}).AddRow(
			"PermissionSet",
			`{"writeValidation":{"validators":[],"ownerResolution":{"paths":[{"hops":[{"self":true,"gate":{"field":"IsOwnedByProfile","is":true}}]}]}}}`,
		))

	result := &AssertionResult{}
	AssertManagedOwnerResolutionReconciled(db, result, map[string]struct{}{"permissionset": {}})
	if len(result.Violations) != 0 {
		t.Fatalf("violations = %d, want 0: %+v", len(result.Violations), result.Violations)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	query := regexp.QuoteMeta("SELECT `ApiName`, `AccessPolicy` FROM `_System_Object` WHERE COALESCE(`IsDeleted`, 0) = 0")
	// No row for the expected table at all.
	mock.ExpectQuery(query).
		WillReturnRows(sqlmock.NewRows([]string{"ApiName", "AccessPolicy"}).AddRow("Profile", `{}`))

	result := &AssertionResult{}
	AssertManagedOwnerResolutionReconciled(db, result, map[string]struct{}{"permissionset": {}})
	if len(result.Violations) != 1 {
		t.Fatalf("violations = %d, want 1: %+v", len(result.Violations), result.Violations)
	}
	v := result.Violations[0]
	if v.Category != "ManagedOwnerResolutionReconciliation" || v.Severity != constants.SeverityError {
		t.Fatalf("violation = %#v, want managed owner-resolution reconciliation error", v)
	}
	if !strings.Contains(v.Description, "absent from _System_Object") {
		t.Fatalf("description = %q, want absent-from-_System_Object message", v.Description)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	query := regexp.QuoteMeta("SELECT `ApiName`, `AccessPolicy` FROM `_System_Object` WHERE COALESCE(`IsDeleted`, 0) = 0")
	mock.ExpectQuery(query).
		WillReturnRows(sqlmock.NewRows([]string{"ApiName", "AccessPolicy"}).AddRow(
			constants.TableConnectedApp,
			`{}`,
		))

	result := &AssertionResult{}
	AssertManagedWriteValidationCapabilitiesReconciled(db, result, map[string]*models.RecordWriteValidationAccessPolicy{
		strings.ToLower(constants.TableConnectedApp): {
			Validators: []models.RecordWriteValidationPolicy{
				{Phase: "before_create", Capability: "platform.security.write.connected-app-policy"},
			},
			Canonicalizers: []models.RecordWriteCanonicalizationPolicy{
				{Phase: "before_create", Capability: "platform.security.write.connected-app-policy-canonicalization"},
			},
		},
	})
	if len(result.Violations) != 1 {
		t.Fatalf("violations = %d, want 1: %+v", len(result.Violations), result.Violations)
	}
	v := result.Violations[0]
	if v.Category != "ManagedWriteValidationReconciliation" || v.Severity != constants.SeverityError {
		t.Fatalf("violation = %#v, want managed write-validation reconciliation error", v)
	}
	if v.Object != strings.ToLower(constants.TableConnectedApp) {
		t.Fatalf("object = %q, want %s", v.Object, strings.ToLower(constants.TableConnectedApp))
	}
	if !strings.Contains(v.Description, "platform.security.write.connected-app-policy") || !strings.Contains(v.Description, "setup apply") {
		t.Fatalf("description = %q, want missing ConnectedApp capability + setup apply guidance", v.Description)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	query := regexp.QuoteMeta("SELECT `ApiName`, `AccessPolicy` FROM `_System_Object` WHERE COALESCE(`IsDeleted`, 0) = 0")
	mock.ExpectQuery(query).
		WillReturnRows(sqlmock.NewRows([]string{"ApiName", "AccessPolicy"}).AddRow(
			constants.TableConnectedApp,
			`{"writeValidation":{"validators":[{"phase":"before_create","capability":"platform.security.write.connected-app-policy"}],"canonicalizers":[{"phase":"before_create","capability":"platform.security.write.connected-app-policy-canonicalization"}]}}`,
		))

	result := &AssertionResult{}
	AssertManagedWriteValidationCapabilitiesReconciled(db, result, map[string]*models.RecordWriteValidationAccessPolicy{
		strings.ToLower(constants.TableConnectedApp): {
			Validators: []models.RecordWriteValidationPolicy{
				{Phase: "before_create", Capability: "platform.security.write.connected-app-policy"},
			},
			Canonicalizers: []models.RecordWriteCanonicalizationPolicy{
				{Phase: "before_create", Capability: "platform.security.write.connected-app-policy-canonicalization"},
			},
		},
	})
	if len(result.Violations) != 0 {
		t.Fatalf("violations = %d, want 0: %+v", len(result.Violations), result.Violations)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	truePtr := func(v bool) *bool { return &v }
	falsePtr := func(v bool) *bool { return &v }
	def := domainschema.ManagedRelationshipSemantics{
		ChildObjectAPIName:  "Contact",
		ParentObjectAPIName: "Account",
		FieldAPIName:        "AccountId",
		IsSharingParent:     truePtr(true),
		CascadeDelete:       falsePtr(false),
	}

	t.Run("missing explicit row is tolerated", func(t *testing.T) {
		mock.ExpectQuery("SELECT IsSharingParent, CascadeDelete, RestrictedDelete\\s+FROM _System_Relationship").
			WithArgs("contact", "account", "accountid").
			WillReturnRows(sqlmock.NewRows([]string{"IsSharingParent", "CascadeDelete", "RestrictedDelete"}))

		result := &AssertionResult{}
		AssertManagedRelationshipConsistency(db, result, []domainschema.ManagedRelationshipSemantics{def})
		if len(result.Violations) != 0 {
			t.Fatalf("violations = %#v, want none for missing explicit overlay row", result.Violations)
		}
	})

	t.Run("existing explicit row drift still fails closed", func(t *testing.T) {
		mock.ExpectQuery("SELECT IsSharingParent, CascadeDelete, RestrictedDelete\\s+FROM _System_Relationship").
			WithArgs("contact", "account", "accountid").
			WillReturnRows(sqlmock.NewRows([]string{"IsSharingParent", "CascadeDelete", "RestrictedDelete"}).
				AddRow(false, true, false))

		result := &AssertionResult{}
		AssertManagedRelationshipConsistency(db, result, []domainschema.ManagedRelationshipSemantics{def})
		if len(result.Violations) == 0 {
			t.Fatal("violations = 0, want drift violation for explicit row mismatch")
		}
		if result.Violations[0].Category != "ManagedRelationshipDrift" || result.Violations[0].Severity != constants.SeverityError {
			t.Fatalf("first violation = %#v, want managed relationship error drift", result.Violations[0])
		}
	})

	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestAssertManagedRelationshipConsistencyReportsMissingOverlayRow(t *testing.T) {
	// When a managed definition requires an overlay row (plain reference field
	// with IsSharingParent=true), a missing row should be reported as drift.
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	truePtr := func(v bool) *bool { return &v }

	mock.ExpectQuery("SELECT IsSharingParent").
		WithArgs("dashboardcomponent", "dashboard", "dashboardid").
		WillReturnRows(sqlmock.NewRows([]string{"IsSharingParent", "CascadeDelete", "RestrictedDelete"}))

	result := &AssertionResult{}
	AssertManagedRelationshipConsistency(db, result, []domainschema.ManagedRelationshipSemantics{
		{
			ChildObjectAPIName:  "DashboardComponent",
			ParentObjectAPIName: "Dashboard",
			FieldAPIName:        "DashboardId",
			IsSharingParent:     truePtr(true),
			CascadeDelete:       truePtr(true),
		},
	}, []domainschema.TableDefinition{
		{
			TableName: "DashboardComponent",
			Columns: []domainschema.ColumnDefinition{
				{
					Name:             "DashboardId",
					LogicalType:      string(constants.FieldTypeReference),
					ReferenceTo:      []string{"Dashboard"},
					RelationshipName: "Dashboard",
				},
			},
		},
	})

	if len(result.Violations) == 0 {
		t.Fatalf("expected violation for missing overlay row, got none")
	}
	found := false
	for _, v := range result.Violations {
		if v.Category == "ManagedRelationshipDrift" && strings.Contains(v.Object, "DashboardComponent") {
			found = true
			if !strings.Contains(v.Description, "overlay row missing") {
				t.Errorf("violation description = %q, want overlay row missing", v.Description)
			}
		}
	}
	if !found {
		t.Errorf("no ManagedRelationshipDrift violation found for DashboardComponent")
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestAssertManagedRelationshipConsistencyToleratesMissingRowForMasterDetail(t *testing.T) {
	// When a managed definition's backing field is master-detail, a missing
	// overlay row is benign because the resolver derives semantics from the
	// field type alone.
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	truePtr := func(v bool) *bool { return &v }

	mock.ExpectQuery("SELECT IsSharingParent").
		WithArgs("contact", "account", "accountid").
		WillReturnRows(sqlmock.NewRows([]string{"IsSharingParent", "CascadeDelete", "RestrictedDelete"}))

	result := &AssertionResult{}
	AssertManagedRelationshipConsistency(db, result, []domainschema.ManagedRelationshipSemantics{
		{
			ChildObjectAPIName:  "Contact",
			ParentObjectAPIName: "Account",
			FieldAPIName:        "AccountId",
			IsSharingParent:     truePtr(true),
		},
	}, []domainschema.TableDefinition{
		{
			TableName: "Contact",
			Columns: []domainschema.ColumnDefinition{
				{
					Name:             "AccountId",
					LogicalType:      string(constants.FieldTypeMasterDetail),
					ReferenceTo:      []string{"Account"},
					RelationshipName: "Account",
					IsMasterDetail:   true,
				},
			},
		},
	})

	if len(result.Violations) != 0 {
		t.Errorf("expected no violations for master-detail field missing overlay row, got %d: %+v", len(result.Violations), result.Violations)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}
