package catalogsystem

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

	"github.com/DATA-DOG/go-sqlmock"
	catalogpublicationv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/catalogpublicationv1"
	layoutbundlekeyv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/layoutbundlekeyv1"
	layoutbundleownerverificationv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/layoutbundleownerverificationv1"
	systemauthorityv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/systemlayoutbundleauthorityv1"
	"github.com/pingcap-inc/nexuscrm-controlplane/catalogpublication"
	"github.com/pingcap-inc/nexuscrm-controlplane/internal/catalogsystemfence"
	"github.com/pingcap-inc/nexuscrm-controlplane/internal/catalogverificationtx"
)

func TestDeriveSystemLayoutBundleOwnerValuesBindsSourceAndLockedAuthority(
	t *testing.T,
) {
	source, owner := systemOwnerVerificationFixture(t)
	if err := validateSystemOwnerFactAgainstSource(source, owner); err != nil {
		t.Fatal(err)
	}

	evidence, evidenceDigest, receipt, receiptDigest, err :=
		deriveSystemLayoutBundleOwnerValues(source, owner)
	if err != nil {
		t.Fatal(err)
	}
	if err := systemauthorityv1.ValidateEvidence(evidence); err != nil {
		t.Fatal(err)
	}
	if evidence.EvidenceVersion != systemauthorityv1.EvidenceVersion ||
		evidence.SourceCatalogSnapshot != source.SourceCatalogSnapshot ||
		evidence.BundleKey != owner.Slot.BundleKey ||
		evidence.SlotValueVersion != owner.Slot.ValueVersion ||
		evidence.SlotState != systemauthorityv1.SlotStateActive {
		t.Fatalf("evidence = %#v", evidence)
	}
	recomputedEvidence, err := systemauthorityv1.EvidenceDigest(evidence)
	if err != nil || recomputedEvidence != evidenceDigest {
		t.Fatalf("evidence digest = %q, %v", recomputedEvidence, err)
	}
	if err := layoutbundleownerverificationv1.ValidateReceipt(receipt); err != nil {
		t.Fatal(err)
	}
	if receipt.OrgID != source.OrgID ||
		receipt.WriterCommandID != source.WriterCommandID ||
		receipt.WriterReservationGeneration != source.WriterReservationGeneration ||
		receipt.SourceRequestDigest != source.SourceRequestDigest ||
		receipt.RequiredBundleKeySetDigest != source.RequiredBundleKeySetDigest ||
		receipt.BundleKey != source.BundleKey ||
		receipt.EvidenceVersion != evidence.EvidenceVersion ||
		receipt.EvidenceDigest != evidenceDigest ||
		receipt.VerificationState != layoutbundleownerverificationv1.VerificationStateVerified {
		t.Fatalf("receipt = %#v", receipt)
	}
	recomputedReceipt, err := layoutbundleownerverificationv1.ReceiptDigest(receipt)
	if err != nil || recomputedReceipt != receiptDigest {
		t.Fatalf("receipt digest = %q, %v", recomputedReceipt, err)
	}
}

func TestVerifiedSystemEvidenceCapabilityDiesWithRetainedSourceLease(
	t *testing.T,
) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()
	mock.ExpectQuery(regexp.QuoteMeta(
		"SELECT @@SESSION.tidb_txn_mode",
	)).WillReturnRows(
		sqlmock.NewRows([]string{"tidb_txn_mode"}).AddRow("optimistic"),
	)
	mock.ExpectExec(regexp.QuoteMeta(
		"SET SESSION tidb_txn_mode = 'pessimistic'",
	)).WillReturnResult(sqlmock.NewResult(0, 0))
	conn, err := db.Conn(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	mode, err := catalogsystemfence.ForcePessimistic(context.Background(), conn)
	if err != nil {
		t.Fatal(err)
	}
	mock.ExpectBegin()
	tx, err := conn.BeginTx(context.WithoutCancel(context.Background()), nil)
	if err != nil {
		t.Fatal(err)
	}
	token, err := catalogverificationtx.NewOwned(db, conn, tx, mode)
	if err != nil {
		t.Fatal(err)
	}
	source, owner := systemOwnerVerificationFixture(t)
	bindSystemVerificationSource(t, token, source)
	lease, err := catalogverificationtx.NewLease(
		token,
		systemRequiredSet(source.BundleKey),
	)
	if err != nil {
		t.Fatal(err)
	}
	command := testDurableCommand(t, testCommand1, testCreateRequest("1"), testPrincipal)
	outcome := mustOutcome(t, command, ResultApplied)
	claim, err := lease.BeginSystemClaim(
		0,
		source,
		catalogverificationtx.SystemOwnerRequest{BundleKey: source.BundleKey},
	)
	if err != nil {
		t.Fatal(err)
	}
	expectSystemOwnerVerificationRows(mock, owner.Slot, owner.SlotDigest, command, outcome)
	lockedOwner, err := catalogverificationtx.VerifySystemOwnerClaim(
		context.Background(),
		db,
		claim,
	)
	if err != nil {
		t.Fatal(err)
	}
	if lockedOwner != owner {
		t.Fatalf("locked owner = %#v, want %#v", lockedOwner, owner)
	}
	evidence, evidenceDigest, receipt, receiptDigest, err :=
		deriveSystemLayoutBundleOwnerValues(source, lockedOwner)
	if err != nil {
		t.Fatal(err)
	}
	expectSystemFenceOwnership(mock)
	if err := catalogverificationtx.CompleteSystemClaim(claim, receipt); err != nil {
		t.Fatal(err)
	}
	capability := &VerifiedSystemLayoutBundleEvidenceCapability{
		authority:      &authorityIdentity{},
		claim:          claim,
		evidence:       evidence,
		evidenceDigest: evidenceDigest,
		receipt:        receipt,
		receiptDigest:  receiptDigest,
		slotDigest:     lockedOwner.SlotDigest,
	}
	expectSystemFenceOwnership(mock)
	if err := capability.Validate(); err != nil {
		t.Fatal(err)
	}
	expectSystemFenceOwnership(mock)
	if got, err := capability.Evidence(); err != nil || got != evidence {
		t.Fatalf("live evidence = %#v, %v", got, err)
	}
	expectSystemFenceOwnership(mock)
	if got, err := capability.Receipt(); err != nil || got != receipt {
		t.Fatalf("live receipt = %#v, %v", got, err)
	}
	expectSystemFenceOwnership(mock)
	if got, err := capability.EvidenceDigest(); err != nil ||
		got != evidenceDigest {
		t.Fatalf("live evidence digest = %q, %v", got, err)
	}
	expectSystemFenceOwnership(mock)
	if got, err := capability.ReceiptDigest(); err != nil ||
		got != receiptDigest {
		t.Fatalf("live receipt digest = %q, %v", got, err)
	}

	mock.ExpectRollback()
	mock.ExpectQuery(regexp.QuoteMeta(
		"SELECT RELEASE_LOCK('" + catalogsystemfence.Name + "')",
	)).WillReturnRows(sqlmock.NewRows([]string{"RELEASE_LOCK"}).AddRow(1))
	mock.ExpectExec(regexp.QuoteMeta(
		"SET SESSION tidb_txn_mode = ?",
	)).WithArgs("optimistic").WillReturnResult(sqlmock.NewResult(0, 0))
	if err := lease.Close(); err != nil {
		t.Fatal(err)
	}
	if claim.Active() {
		t.Fatal("system source claim remained active after source lease close")
	}
	if err := capability.Validate(); !errors.Is(err, ErrConflict) {
		t.Fatalf("closed capability validation = %v, want ErrConflict", err)
	}
	if _, err := capability.Evidence(); !errors.Is(err, ErrConflict) {
		t.Fatalf("closed capability exposed evidence: %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatal(err)
	}
}

func TestVerifiedSystemEvidenceCapabilityRejectsDroppedAuthoritySession(
	t *testing.T,
) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatal(err)
	}
	defer db.Close()
	mock.ExpectQuery(regexp.QuoteMeta(
		"SELECT @@SESSION.tidb_txn_mode",
	)).WillReturnRows(
		sqlmock.NewRows([]string{"tidb_txn_mode"}).AddRow("optimistic"),
	)
	mock.ExpectExec(regexp.QuoteMeta(
		"SET SESSION tidb_txn_mode = 'pessimistic'",
	)).WillReturnResult(sqlmock.NewResult(0, 0))
	conn, err := db.Conn(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	mode, err := catalogsystemfence.ForcePessimistic(context.Background(), conn)
	if err != nil {
		t.Fatal(err)
	}
	mock.ExpectBegin()
	tx, err := conn.BeginTx(context.WithoutCancel(context.Background()), nil)
	if err != nil {
		t.Fatal(err)
	}
	token, err := catalogverificationtx.NewOwned(db, conn, tx, mode)
	if err != nil {
		t.Fatal(err)
	}
	source, owner := systemOwnerVerificationFixture(t)
	bindSystemVerificationSource(t, token, source)
	lease, err := catalogverificationtx.NewLease(
		token,
		systemRequiredSet(source.BundleKey),
	)
	if err != nil {
		t.Fatal(err)
	}
	command := testDurableCommand(t, testCommand1, testCreateRequest("1"), testPrincipal)
	outcome := mustOutcome(t, command, ResultApplied)
	claim, err := lease.BeginSystemClaim(
		0,
		source,
		catalogverificationtx.SystemOwnerRequest{BundleKey: source.BundleKey},
	)
	if err != nil {
		t.Fatal(err)
	}
	expectSystemOwnerVerificationRows(mock, owner.Slot, owner.SlotDigest, command, outcome)
	lockedOwner, err := catalogverificationtx.VerifySystemOwnerClaim(
		context.Background(),
		db,
		claim,
	)
	if err != nil {
		t.Fatal(err)
	}
	evidence, evidenceDigest, receipt, receiptDigest, err :=
		deriveSystemLayoutBundleOwnerValues(source, lockedOwner)
	if err != nil {
		t.Fatal(err)
	}
	expectSystemFenceOwnership(mock)
	if err := catalogverificationtx.CompleteSystemClaim(claim, receipt); err != nil {
		t.Fatal(err)
	}
	capability := &VerifiedSystemLayoutBundleEvidenceCapability{
		authority:      &authorityIdentity{},
		claim:          claim,
		evidence:       evidence,
		evidenceDigest: evidenceDigest,
		receipt:        receipt,
		receiptDigest:  receiptDigest,
		slotDigest:     lockedOwner.SlotDigest,
	}
	expectSystemFenceOwnership(mock)
	if err := capability.Validate(); err != nil {
		t.Fatal(err)
	}

	sessionLost := errors.New("retained authority session dropped")
	mock.ExpectQuery(regexp.QuoteMeta(
		"SELECT IS_USED_LOCK('" + catalogsystemfence.Name + "'), CONNECTION_ID()",
	)).WillReturnError(sessionLost)
	mock.ExpectRollback()
	if err := capability.Validate(); !errors.Is(err, ErrConflict) {
		t.Fatalf("dropped-session capability validation = %v, want ErrConflict", err)
	}
	if got, err := capability.Evidence(); !errors.Is(err, ErrConflict) ||
		got != (systemauthorityv1.SystemLayoutBundleAuthorityEvidenceV1{}) {
		t.Fatalf("dropped-session evidence = %#v, %v", got, err)
	}
	if got, err := capability.Receipt(); !errors.Is(err, ErrConflict) ||
		got != (layoutbundleownerverificationv1.Receipt{}) {
		t.Fatalf("dropped-session receipt = %#v, %v", got, err)
	}
	if got, err := capability.EvidenceDigest(); !errors.Is(err, ErrConflict) ||
		got != "" {
		t.Fatalf("dropped-session evidence digest = %q, %v", got, err)
	}
	if got, err := capability.ReceiptDigest(); !errors.Is(err, ErrConflict) ||
		got != "" {
		t.Fatalf("dropped-session receipt digest = %q, %v", got, err)
	}
	if token.ActiveFor(db) || lease.Active() || claim.Active() {
		t.Fatal("dropped authority session left a source or owner capability active")
	}
	if err := lease.Close(); err != nil {
		t.Fatalf("close invalidated lease = %v", err)
	}
	if open := db.Stats().OpenConnections; open != 0 {
		t.Fatalf("dropped authority session retained %d pooled connections", open)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatal(err)
	}
}

func systemRequiredSet(
	key layoutbundlekeyv1.BundleKey,
) layoutbundlekeyv1.RequiredBundleKeySet {
	return layoutbundlekeyv1.RequiredBundleKeySet{
		Version:    layoutbundlekeyv1.RequiredBundleKeySetVersion,
		BundleKeys: []layoutbundlekeyv1.BundleKey{key},
	}
}

func expectSystemOwnerVerificationRows(
	mock sqlmock.Sqlmock,
	slot systemauthorityv1.SystemLayoutBundleSlotV1,
	slotDigest string,
	command durableCommand,
	outcome durableOutcome,
) {
	key := slot.BundleKey
	keyArgs := []driver.Value{
		key.BundleKeyVersion,
		string(key.SlotOwnerKind),
		key.SlotOwnerNamespace,
		string(key.LocalComponentKey),
	}
	mock.ExpectQuery(regexp.QuoteMeta(
		"SELECT GET_LOCK('" + catalogsystemfence.Name + "', 600)",
	)).WillReturnRows(sqlmock.NewRows([]string{"GET_LOCK"}).AddRow(1))
	mock.ExpectQuery(regexp.QuoteMeta(
		"SELECT CONNECTION_ID()",
	)).WillReturnRows(
		sqlmock.NewRows([]string{"CONNECTION_ID"}).AddRow(73),
	)
	mock.ExpectQuery(`(?s)FROM _System_CatalogSystemLayoutBundleSlot.*FOR UPDATE`).
		WithArgs(keyArgs...).
		WillReturnRows(sqlmock.NewRows(currentColumns).AddRow(
			key.BundleKeyVersion,
			string(key.SlotOwnerKind),
			[]byte(key.SlotOwnerNamespace),
			[]byte(key.LocalComponentKey),
			slot.ValueVersion,
			string(slot.State),
			slotDigest,
			string(command.CommandID),
		))
	mock.ExpectQuery(`(?s)FROM _System_CatalogSystemLayoutBundleSlotHistory.*ORDER BY TransitionOrdinal FOR UPDATE`).
		WithArgs(keyArgs...).
		WillReturnRows(sqlmock.NewRows(historyColumns).AddRow(
			key.BundleKeyVersion,
			string(key.SlotOwnerKind),
			[]byte(key.SlotOwnerNamespace),
			[]byte(key.LocalComponentKey),
			[]byte("1"),
			string(command.CommandID),
			slot.ValueVersion,
			string(slot.State),
			slotDigest,
			nil,
		))
	request := command.Request
	mock.ExpectQuery(`(?s)FROM _System_CatalogSystemLayoutBundleSlotCommand.*ORDER BY CommandId FOR UPDATE`).
		WithArgs(keyArgs...).
		WillReturnRows(sqlmock.NewRows(commandColumns).AddRow(
			string(command.CommandID),
			[]byte(command.Principal.PrincipalID),
			command.Principal.AuditDigest,
			command.RequestDigest,
			string(request.Operation),
			request.TargetBundleKey.BundleKeyVersion,
			string(request.TargetBundleKey.SlotOwnerKind),
			[]byte(request.TargetBundleKey.SlotOwnerNamespace),
			[]byte(request.TargetBundleKey.LocalComponentKey),
			request.DesiredSlotValue.ValueVersion,
			request.DesiredSlotValue.BundleKey.BundleKeyVersion,
			string(request.DesiredSlotValue.BundleKey.SlotOwnerKind),
			[]byte(request.DesiredSlotValue.BundleKey.SlotOwnerNamespace),
			[]byte(request.DesiredSlotValue.BundleKey.LocalComponentKey),
			string(request.DesiredSlotValue.State),
			string(request.ExpectedPrior.Variant),
			nil,
		))
	mock.ExpectQuery(`(?s)FROM _System_CatalogSystemLayoutBundleSlotCommandOutcome.*FOR UPDATE`).
		WithArgs(string(command.CommandID)).
		WillReturnRows(sqlmock.NewRows(outcomeColumns).AddRow(
			string(outcome.CommandID),
			outcome.RequestDigest,
			[]byte(outcome.Principal.PrincipalID),
			outcome.Principal.AuditDigest,
			string(outcome.Result),
			outcome.OutcomeDigest,
		))
}

func expectSystemFenceOwnership(mock sqlmock.Sqlmock) {
	mock.ExpectQuery(regexp.QuoteMeta(
		"SELECT IS_USED_LOCK('" + catalogsystemfence.Name + "'), CONNECTION_ID()",
	)).WillReturnRows(
		sqlmock.NewRows([]string{"IS_USED_LOCK", "CONNECTION_ID"}).
			AddRow(73, 73),
	)
}

func TestSystemOwnerFactValidationBindsPrivateDigestAndSourceIdentity(t *testing.T) {
	source, owner := systemOwnerVerificationFixture(t)
	if err := validateSystemOwnerFactAgainstSource(source, owner); err != nil {
		t.Fatal(err)
	}

	digestDrift := owner
	digestDrift.SlotDigest = testDigest("9")
	if err := validateSystemOwnerFactAgainstSource(source, digestDrift); !errors.Is(
		err,
		ErrCorrupt,
	) {
		t.Fatalf("slot digest drift = %v", err)
	}

	foreignKey := source
	foreignKey.BundleKey.LocalComponentKey = layoutbundlekeyv1.LocalComponentKey(
		"key256:" + strings.Repeat("2", 64),
	)
	if err := validateSystemOwnerFactAgainstSource(foreignKey, owner); !errors.Is(
		err,
		ErrConflict,
	) {
		t.Fatalf("foreign source key = %v", err)
	}

	foreignSnapshot := source
	foreignSnapshot.SourceCatalogSnapshot.OrgID = "org-system-2"
	if err := validateSystemOwnerFactAgainstSource(foreignSnapshot, owner); !errors.Is(
		err,
		ErrConflict,
	) {
		t.Fatalf("foreign source snapshot = %v", err)
	}

	tombstone := owner
	tombstone.Slot.State = systemauthorityv1.SlotStateTombstone
	tombstone.SlotDigest = mustSlotDigest(t, tombstone.Slot)
	if err := validateSystemOwnerFactAgainstSource(source, tombstone); !errors.Is(
		err,
		ErrCorrupt,
	) {
		t.Fatalf("tombstoned owner fact = %v", err)
	}
}

func TestVerifiedSystemEvidenceCapabilityIsOpaqueAndHasNoWireForm(t *testing.T) {
	capabilityType := reflect.TypeOf(VerifiedSystemLayoutBundleEvidenceCapability{})
	for index := 0; index < capabilityType.NumField(); index++ {
		if capabilityType.Field(index).PkgPath == "" {
			t.Fatalf("capability field %q is exported", capabilityType.Field(index).Name)
		}
	}
	capability := &VerifiedSystemLayoutBundleEvidenceCapability{}
	if err := capability.Validate(); !errors.Is(err, ErrConflict) {
		t.Fatalf("zero capability validation = %v", err)
	}
	if _, err := json.Marshal(capability); !errors.Is(
		err,
		errVerifiedSystemEvidenceHasNoJSON,
	) {
		t.Fatalf("capability JSON marshal = %v", err)
	}
	if err := json.Unmarshal([]byte(`{}`), capability); !errors.Is(
		err,
		errVerifiedSystemEvidenceHasNoJSON,
	) {
		t.Fatalf("capability JSON unmarshal = %v", err)
	}
}

func TestSystemOwnerVerifierPublicSurfaceIsFixedPurposeAndWriteFree(t *testing.T) {
	method, found := reflect.TypeOf((*Authority)(nil)).MethodByName(
		"VerifyLayoutBundleOwner",
	)
	if !found {
		t.Fatal("VerifyLayoutBundleOwner is absent")
	}
	expected := []reflect.Type{
		reflect.TypeOf((*Authority)(nil)),
		reflect.TypeOf((*context.Context)(nil)).Elem(),
		reflect.TypeOf((*catalogpublication.CandidateSourceVerificationLease)(nil)),
		reflect.TypeOf(layoutbundlekeyv1.BundleKey{}),
	}
	if method.Type.NumIn() != len(expected) {
		t.Fatalf("VerifyLayoutBundleOwner inputs = %s", method.Type)
	}
	for index, wanted := range expected {
		if method.Type.In(index) != wanted {
			t.Fatalf(
				"VerifyLayoutBundleOwner input[%d] = %s, want %s",
				index,
				method.Type.In(index),
				wanted,
			)
		}
	}

	_, file, _, ok := runtime.Caller(0)
	if !ok {
		t.Fatal("resolve verifier test file")
	}
	raw, err := os.ReadFile(filepath.Join(
		filepath.Dir(file),
		"layout_bundle_owner_verification.go",
	))
	if err != nil {
		t.Fatal(err)
	}
	for _, forbidden := range []string{
		"database/sql",
		"_System_",
		"QueryContext",
		"QueryRowContext",
		"ExecContext",
		"INSERT ",
		"UPDATE ",
		"DELETE ",
		"ProofAdapter",
		"VerificationCallback",
	} {
		if strings.Contains(string(raw), forbidden) {
			t.Fatalf("public system verifier contains forbidden seam %q", forbidden)
		}
	}
}

func TestMapSystemOwnerVerificationError(t *testing.T) {
	for name, test := range map[string]struct {
		input error
		want  error
	}{
		"not found": {
			input: catalogverificationtx.ErrSystemOwnerNotFound,
			want:  ErrNotFound,
		},
		"owner conflict": {
			input: catalogverificationtx.ErrSystemOwnerConflict,
			want:  ErrConflict,
		},
		"closed source": {
			input: catalogverificationtx.ErrClosed,
			want:  ErrConflict,
		},
		"owner corrupt": {
			input: catalogverificationtx.ErrSystemOwnerCorrupt,
			want:  ErrCorrupt,
		},
		"source corrupt": {
			input: catalogpublication.ErrSourceCorrupt,
			want:  ErrCorrupt,
		},
	} {
		t.Run(name, func(t *testing.T) {
			if err := mapSystemOwnerVerificationError(test.input); !errors.Is(
				err,
				test.want,
			) {
				t.Fatalf("mapped error = %v, want %v", err, test.want)
			}
		})
	}
}

func systemOwnerVerificationFixture(
	t *testing.T,
) (
	catalogverificationtx.SystemSourceFacts,
	catalogverificationtx.SystemOwnerFact,
) {
	t.Helper()
	slot := testSlotValue("1", systemauthorityv1.SlotStateActive)
	portableKey := portableSystemBundleKey(slot.BundleKey)
	requiredDigest, err := layoutbundlekeyv1.RequiredBundleKeySetDigest(
		systemRequiredSet(portableKey),
	)
	if err != nil {
		t.Fatal(err)
	}
	source := catalogverificationtx.SystemSourceFacts{
		SourceCatalogSnapshot: catalogpublicationv1.CatalogSnapshotBinding{
			OrgID:                 "org-system-1",
			CatalogRevision:       7,
			CatalogSnapshotDigest: testDigest("4"),
			SchemaGeneration:      5,
			EngineGeneration:      6,
		},
		OrgID:                       "org-system-1",
		WriterCommandID:             string(testCommand1),
		WriterReservationGeneration: 3,
		SourceRequestDigest:         testDigest("5"),
		RequiredBundleKeySetDigest:  requiredDigest,
		BundleKey:                   portableKey,
	}
	owner := catalogverificationtx.SystemOwnerFact{
		Slot:       slot,
		SlotDigest: mustSlotDigest(t, slot),
	}
	return source, owner
}

func bindSystemVerificationSource(
	t *testing.T,
	token *catalogverificationtx.Token,
	source catalogverificationtx.SystemSourceFacts,
) {
	t.Helper()
	if err := token.BindValidatedSource(
		source.SourceCatalogSnapshot,
		source.OrgID,
		source.WriterCommandID,
		source.WriterReservationGeneration,
		source.SourceRequestDigest,
		source.RequiredBundleKeySetDigest,
	); err != nil {
		t.Fatal(err)
	}
}
