package runtimegrpc

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

	identitysemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/identityv1"
	querysemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/queryv1"
	accesssemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordaccessv1"
	attachmentsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordattachmentsv1"
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordsv1"
	recordviewsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordviewv1"
	recyclemutationsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recyclebinmutationv1"
	recyclesemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recyclebinv1"
	relatedsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/relatedcountsv1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/envelope"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/mapping"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

func TestSelectedRootClientOwnsProtocolEnvelopeAndSemanticMapping(t *testing.T) {
	value := "root"
	var calls int
	client, err := NewSelectedRootClient(func(ctx context.Context, request *runtimev1.ReadSelectedRootRequest, options ...grpc.CallOption) (*runtimev1.ReadSelectedRootResponse, error) {
		calls++
		values, ok := metadata.FromOutgoingContext(ctx)
		if !ok {
			t.Fatal("outgoing metadata is missing")
		}
		assertExactly(t, values.Get(envelope.ProtocolMajorMetadataKey), "1")
		assertExactly(t, values.Get(envelope.ProtocolMinorMetadataKey), "0")
		assertExactly(t, values.Get(envelope.CapabilityMetadataKey), semantic.CapabilityName)
		assertExactly(t, values.Get("traceparent"), "trace-1")
		assertClientCallOptions(t, options)
		if request.GetObjectId() != "Issue" || request.GetRecordId() != "ISS-1" || !reflect.DeepEqual(request.GetSelectedRootFields(), []string{"Subject"}) {
			t.Fatalf("wire request = %#v", request)
		}
		return mapping.ToWireResponse(semantic.Response{
			RecordID: "ISS-1",
			Fields:   []semantic.Field{{Name: "Subject", Kind: semantic.ValueKindString, String: &value}},
			Receipts: semantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: semantic.CapabilityName},
		})
	})
	if err != nil {
		t.Fatal(err)
	}
	ctx, cancel := context.WithTimeout(metadata.NewOutgoingContext(context.Background(), metadata.Pairs(
		envelope.ProtocolMajorMetadataKey, "9",
		envelope.CapabilityMetadataKey, "duplicate",
		"traceparent", "trace-1",
	)), time.Second)
	defer cancel()
	response, err := client.ReadSelectedRoot(ctx, semantic.Request{
		ObjectID: "Issue", RecordID: "ISS-1", SelectedRootFields: []string{"Subject"},
	})
	if err != nil {
		t.Fatal(err)
	}
	if calls != 1 || response.RecordID != "ISS-1" || len(response.Fields) != 1 || response.Fields[0].String == nil || *response.Fields[0].String != "root" {
		t.Fatalf("calls=%d response=%#v", calls, response)
	}
}

func TestSelectedRootClientRejectsBeforeRPCAndDecodesStableFailure(t *testing.T) {
	var calls int
	client, err := NewSelectedRootClient(func(context.Context, *runtimev1.ReadSelectedRootRequest, ...grpc.CallOption) (*runtimev1.ReadSelectedRootResponse, error) {
		calls++
		return nil, mapping.ToStatusError(&semantic.Failure{Category: semantic.FailureMissing})
	})
	if err != nil {
		t.Fatal(err)
	}
	if _, err := client.ReadSelectedRoot(context.Background(), semantic.Request{
		ObjectID: "Issue", RecordID: "ISS-1", SelectedRootFields: []string{"Subject"},
	}); err == nil || calls != 0 {
		t.Fatalf("missing deadline error=%v calls=%d", err, calls)
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	_, err = client.ReadSelectedRoot(ctx, semantic.Request{
		ObjectID: "Issue", RecordID: "ISS-1", SelectedRootFields: []string{"Subject"},
	})
	var failure *semantic.Failure
	if !errors.As(err, &failure) || failure.Category != semantic.FailureMissing || calls != 1 {
		t.Fatalf("error=%#v calls=%d", err, calls)
	}
}

func TestSelectedRootClientConstructionAndUnavailableZeroValue(t *testing.T) {
	if client, err := NewSelectedRootClient(nil); err == nil || client != nil {
		t.Fatalf("NewSelectedRootClient(nil) = %#v, %v", client, err)
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	var client *SelectedRootClient
	_, err := client.ReadSelectedRoot(ctx, semantic.Request{
		ObjectID: "Issue", RecordID: "ISS-1", SelectedRootFields: []string{"Subject"},
	})
	var failure *semantic.Failure
	if !errors.As(err, &failure) || failure.Category != semantic.FailureUnavailable {
		t.Fatalf("zero client error = %#v", err)
	}
}

func TestRecordAccessClientOwnsProtocolEnvelopeAndSemanticMapping(t *testing.T) {
	object := "WorkItem"
	var calls int
	client, err := NewRecordAccessClient(func(ctx context.Context, request *runtimev1.EvaluateRecordAccessRequest, options ...grpc.CallOption) (*runtimev1.EvaluateRecordAccessResponse, error) {
		calls++
		values, ok := metadata.FromOutgoingContext(ctx)
		if !ok {
			t.Fatal("outgoing metadata is missing")
		}
		assertExactly(t, values.Get(envelope.ProtocolMajorMetadataKey), "1")
		assertExactly(t, values.Get(envelope.ProtocolMinorMetadataKey), "0")
		assertExactly(t, values.Get(envelope.CapabilityMetadataKey), accesssemantic.CapabilityName)
		assertExactly(t, values.Get("traceparent"), "trace-access")
		assertClientCallOptions(t, options)
		if request.GetObjectId() != object || !reflect.DeepEqual(request.GetRecordIds(), []string{"W-2", "W-1"}) {
			t.Fatalf("wire request = %#v", request)
		}
		return mapping.ToWireEvaluateRecordAccessResponse(accesssemantic.Request{ObjectID: &object, RecordIDs: []string{"W-2", "W-1"}}, accesssemantic.Response{
			Results: []accesssemantic.Result{
				{RecordID: "W-1", Access: accesssemantic.Access{Read: true, Update: true}},
				{RecordID: "W-2", Access: accesssemantic.Access{Read: true, Locked: true}},
			},
			Receipts: accesssemantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: accesssemantic.CapabilityName},
		})
	})
	if err != nil {
		t.Fatal(err)
	}
	ctx, cancel := context.WithTimeout(metadata.NewOutgoingContext(context.Background(), metadata.Pairs(
		envelope.CapabilityMetadataKey, "stale", "traceparent", "trace-access",
	)), time.Second)
	defer cancel()
	response, err := client.EvaluateRecordAccess(ctx, accesssemantic.Request{ObjectID: &object, RecordIDs: []string{"W-2", "W-1"}})
	if err != nil {
		t.Fatal(err)
	}
	if calls != 1 || len(response.Results) != 2 || response.Results[0].RecordID != "W-1" || !response.Results[1].Access.Locked {
		t.Fatalf("calls=%d response=%#v", calls, response)
	}
}

func TestRecordAccessClientRejectsBeforeRPCAndUsesCapabilityFailureGrammar(t *testing.T) {
	var calls int
	client, err := NewRecordAccessClient(func(context.Context, *runtimev1.EvaluateRecordAccessRequest, ...grpc.CallOption) (*runtimev1.EvaluateRecordAccessResponse, error) {
		calls++
		return nil, mapping.ToRecordAccessStatusError(&accesssemantic.Failure{Category: accesssemantic.FailureForbidden})
	})
	if err != nil {
		t.Fatal(err)
	}
	if _, err := client.EvaluateRecordAccess(context.Background(), accesssemantic.Request{RecordIDs: []string{"W-1"}}); err == nil || calls != 0 {
		t.Fatalf("missing deadline error=%v calls=%d", err, calls)
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	_, err = client.EvaluateRecordAccess(ctx, accesssemantic.Request{RecordIDs: []string{"W-1"}})
	var failure *accesssemantic.Failure
	if !errors.As(err, &failure) || failure.Category != accesssemantic.FailureForbidden || calls != 1 {
		t.Fatalf("error=%#v calls=%d", err, calls)
	}
	var zero *RecordAccessClient
	_, err = zero.EvaluateRecordAccess(ctx, accesssemantic.Request{})
	if !errors.As(err, &failure) || failure.Category != accesssemantic.FailureUnavailable {
		t.Fatalf("zero client error=%#v", err)
	}
	if constructed, err := NewRecordAccessClient(nil); err == nil || constructed != nil {
		t.Fatalf("NewRecordAccessClient(nil) = %#v, %v", constructed, err)
	}
}

func TestQueryClientUsesOneClosedCapabilityForRecordsAndHistory(t *testing.T) {
	var recordsCalls, historyCalls int
	client, err := NewQueryClient(
		func(ctx context.Context, request *runtimev1.QueryRecordsRequest, _ ...grpc.CallOption) (*runtimev1.QueryRecordsResponse, error) {
			recordsCalls++
			values, _ := metadata.FromOutgoingContext(ctx)
			assertExactly(t, values.Get(envelope.CapabilityMetadataKey), querysemantic.CapabilityName)
			return mapping.ToWireQueryRecordsResponse(querysemantic.Response{Done: true, Receipts: querysemantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: querysemantic.CapabilityName}})
		},
		func(ctx context.Context, request *runtimev1.QueryHistoryRequest, _ ...grpc.CallOption) (*runtimev1.QueryHistoryResponse, error) {
			historyCalls++
			values, _ := metadata.FromOutgoingContext(ctx)
			assertExactly(t, values.Get(envelope.CapabilityMetadataKey), querysemantic.CapabilityName)
			return mapping.ToWireQueryHistoryResponse(querysemantic.HistoryResponse{Done: true, Receipts: querysemantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: querysemantic.CapabilityName}})
		},
	)
	if err != nil {
		t.Fatal(err)
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	if _, err := client.QueryRecords(ctx, querysemantic.Request{ObjectID: "Issue", Selections: []querysemantic.Selection{{Kind: querysemantic.SelectionField, Field: "Id"}}}); err != nil {
		t.Fatal(err)
	}
	if _, err := client.QueryHistory(ctx, querysemantic.HistoryRequest{ParentObjectID: "Issue", SelectedFields: []string{"Id"}}); err != nil {
		t.Fatal(err)
	}
	if recordsCalls != 1 || historyCalls != 1 {
		t.Fatalf("records calls=%d history calls=%d", recordsCalls, historyCalls)
	}
	if client, err := NewQueryClient(nil, nil); err == nil || client != nil {
		t.Fatalf("NewQueryClient(nil,nil) = %#v, %v", client, err)
	}
}

func TestRelatedCountClientOwnsOneBoundedCapabilityCall(t *testing.T) {
	var calls int
	client, err := NewRelatedCountClient(func(ctx context.Context, request *runtimev1.CountRelatedRecordsRequest, options ...grpc.CallOption) (*runtimev1.CountRelatedRecordsResponse, error) {
		calls++
		values, _ := metadata.FromOutgoingContext(ctx)
		assertExactly(t, values.Get(envelope.CapabilityMetadataKey), relatedsemantic.CapabilityName)
		assertClientCallOptions(t, options)
		if len(request.GetCounts()) != 1 || request.GetCounts()[0].GetParentRecordId() != "P-1" || request.GetCounts()[0].GetMatchValue() != "parent-key" {
			t.Fatalf("wire request = %#v", request)
		}
		return mapping.ToWireCountRelatedRecordsResponse(relatedsemantic.Request{Counts: []relatedsemantic.CountSpec{{ParentRecordID: "P-1", ChildObjectID: "WorkItem", RelationshipField: "ParentId", MatchValue: "parent-key"}}}, relatedsemantic.Response{
			Results:  []relatedsemantic.Result{{Index: 0, Outcome: relatedsemantic.CountOutcomeCounted, Count: 0}},
			Receipts: relatedsemantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: relatedsemantic.CapabilityName},
		})
	})
	if err != nil {
		t.Fatal(err)
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	response, err := client.CountRelatedRecords(ctx, relatedsemantic.Request{Counts: []relatedsemantic.CountSpec{{ParentRecordID: "P-1", ChildObjectID: "WorkItem", RelationshipField: "ParentId", MatchValue: "parent-key"}}})
	if err != nil || calls != 1 || len(response.Results) != 1 || response.Results[0].Count != 0 {
		t.Fatalf("response=%#v error=%v calls=%d", response, err, calls)
	}
	if constructed, err := NewRelatedCountClient(nil); err == nil || constructed != nil {
		t.Fatalf("NewRelatedCountClient(nil) = %#v, %v", constructed, err)
	}
}

func TestRecycleBinClientOwnsOneBoundedCapabilityCall(t *testing.T) {
	var calls int
	client, err := NewRecycleBinClient(func(ctx context.Context, request *runtimev1.ListRecycleBinRequest, options ...grpc.CallOption) (*runtimev1.ListRecycleBinResponse, error) {
		calls++
		values, _ := metadata.FromOutgoingContext(ctx)
		assertExactly(t, values.Get(envelope.CapabilityMetadataKey), recyclesemantic.CapabilityName)
		assertClientCallOptions(t, options)
		if request.GetScope() != runtimev1.RecycleBinScope_RECYCLE_BIN_SCOPE_MINE {
			t.Fatalf("wire request = %#v", request)
		}
		return mapping.ToWireListRecycleBinResponse(recyclesemantic.Response{
			Items:    []recyclesemantic.Item{{RecordID: "row-1", ObjectID: "WorkItem", DeletedDate: "2026-07-22T04:00:00Z"}},
			Receipts: recyclesemantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: recyclesemantic.CapabilityName},
		})
	})
	if err != nil {
		t.Fatal(err)
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	response, err := client.ListRecycleBin(ctx, recyclesemantic.Request{Scope: recyclesemantic.ScopeMine})
	if err != nil || calls != 1 || len(response.Items) != 1 || response.Items[0].RecordID != "row-1" {
		t.Fatalf("response=%#v error=%v calls=%d", response, err, calls)
	}
	if constructed, err := NewRecycleBinClient(nil); err == nil || constructed != nil {
		t.Fatalf("NewRecycleBinClient(nil) = %#v, %v", constructed, err)
	}
}

func TestRecycleBinMutationClientOwnsRestoreAndPurgeCapabilityCalls(t *testing.T) {
	restoreCalls, purgeCalls := 0, 0
	client, err := NewRecycleBinMutationClient(
		func(ctx context.Context, request *runtimev1.RestoreRecycleBinRequest, options ...grpc.CallOption) (*runtimev1.RestoreRecycleBinResponse, error) {
			restoreCalls++
			values, _ := metadata.FromOutgoingContext(ctx)
			assertExactly(t, values.Get(envelope.CapabilityMetadataKey), recyclemutationsemantic.CapabilityName)
			assertClientCallOptions(t, options)
			semanticRequest, err := mapping.FromWireRestoreRecycleBinRequest(ctx, request)
			if err != nil || semanticRequest.RecordID != "row-1" {
				t.Fatalf("restore request = %#v, %v", semanticRequest, err)
			}
			return mapping.ToWireRestoreRecycleBinResponse(semanticRequest, recyclemutationsemantic.Response{Receipts: recyclemutationsemantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: recyclemutationsemantic.CapabilityName}})
		},
		func(ctx context.Context, request *runtimev1.PurgeRecycleBinRequest, options ...grpc.CallOption) (*runtimev1.PurgeRecycleBinResponse, error) {
			purgeCalls++
			values, _ := metadata.FromOutgoingContext(ctx)
			assertExactly(t, values.Get(envelope.CapabilityMetadataKey), recyclemutationsemantic.CapabilityName)
			assertClientCallOptions(t, options)
			semanticRequest, err := mapping.FromWirePurgeRecycleBinRequest(ctx, request)
			if err != nil || semanticRequest.RecordID != "row-2" {
				t.Fatalf("purge request = %#v, %v", semanticRequest, err)
			}
			return mapping.ToWirePurgeRecycleBinResponse(semanticRequest, recyclemutationsemantic.Response{Receipts: recyclemutationsemantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: recyclemutationsemantic.CapabilityName}})
		},
	)
	if err != nil {
		t.Fatal(err)
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	if _, err := client.RestoreRecycleBin(ctx, recyclemutationsemantic.Request{RecordID: "row-1"}); err != nil {
		t.Fatalf("restore = %v", err)
	}
	if _, err := client.PurgeRecycleBin(ctx, recyclemutationsemantic.Request{RecordID: "row-2"}); err != nil {
		t.Fatalf("purge = %v", err)
	}
	if restoreCalls != 1 || purgeCalls != 1 {
		t.Fatalf("calls = %d/%d, want 1/1", restoreCalls, purgeCalls)
	}
	if constructed, err := NewRecycleBinMutationClient(nil, nil); err == nil || constructed != nil {
		t.Fatalf("NewRecycleBinMutationClient(nil,nil) = %#v, %v", constructed, err)
	}
}

func TestRecordViewClientOwnsBothBoundedCapabilityCalls(t *testing.T) {
	readRequest := recordviewsemantic.ReadRequest{
		ObjectID: "WorkItem", Selector: recordviewsemantic.Selector{Kind: recordviewsemantic.SelectorRecordID, RecordID: "row-1"}, Projection: recordviewsemantic.ProjectionRoot,
	}
	resolveRequest := recordviewsemantic.ResolveAttachmentSourcesRequest{
		ParentObjectID: "WorkItem", ParentRecordID: "row-1", ResolverID: "attachment", SourceValues: []string{"source-1"},
	}
	readCalls, resolveCalls := 0, 0
	client, err := NewRecordViewClient(
		func(ctx context.Context, request *runtimev1.ReadRecordViewRequest, options ...grpc.CallOption) (*runtimev1.ReadRecordViewResponse, error) {
			readCalls++
			values, _ := metadata.FromOutgoingContext(ctx)
			assertExactly(t, values.Get(envelope.CapabilityMetadataKey), recordviewsemantic.CapabilityName)
			assertClientCallOptions(t, options)
			value := "row-1"
			return mapping.ToWireReadRecordViewResponse(readRequest, recordviewsemantic.ReadResponse{
				RecordID: "row-1", Root: recordviewsemantic.Record{ObjectID: "WorkItem", Fields: []recordviewsemantic.Field{{Name: "Id", Value: recordviewsemantic.Value{Kind: recordviewsemantic.ValueString, String: &value}}}},
				Receipts: recordviewsemantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: recordviewsemantic.CapabilityName},
			})
		},
		func(ctx context.Context, request *runtimev1.ResolveRecordAttachmentSourcesRequest, options ...grpc.CallOption) (*runtimev1.ResolveRecordAttachmentSourcesResponse, error) {
			resolveCalls++
			values, _ := metadata.FromOutgoingContext(ctx)
			assertExactly(t, values.Get(envelope.CapabilityMetadataKey), recordviewsemantic.CapabilityName)
			assertClientCallOptions(t, options)
			return mapping.ToWireResolveRecordAttachmentSourcesResponse(resolveRequest, recordviewsemantic.ResolveAttachmentSourcesResponse{
				Results:  []recordviewsemantic.AttachmentSourceResult{{Index: 0, Outcome: recordviewsemantic.AttachmentSourceResolved, FileID: "file-1", FileName: "spec.pdf"}},
				Receipts: recordviewsemantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: recordviewsemantic.CapabilityName},
			})
		},
	)
	if err != nil {
		t.Fatal(err)
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	read, err := client.ReadRecordView(ctx, readRequest)
	if err != nil || read.RecordID != "row-1" || readCalls != 1 {
		t.Fatalf("read=%#v error=%v calls=%d", read, err, readCalls)
	}
	resolved, err := client.ResolveRecordAttachmentSources(ctx, resolveRequest)
	if err != nil || len(resolved.Results) != 1 || resolved.Results[0].FileID != "file-1" || resolveCalls != 1 {
		t.Fatalf("resolved=%#v error=%v calls=%d", resolved, err, resolveCalls)
	}
	if constructed, err := NewRecordViewClient(nil, nil); err == nil || constructed != nil {
		t.Fatalf("NewRecordViewClient(nil,nil) = %#v, %v", constructed, err)
	}
	var nilClient *RecordViewClient
	_, err = nilClient.ReadRecordView(ctx, readRequest)
	var unavailable *recordviewsemantic.Failure
	if !errors.As(err, &unavailable) || unavailable.Category != recordviewsemantic.FailureUnavailable {
		t.Fatalf("nil RecordViewClient read = %v, want unavailable", err)
	}
}

func TestAttachmentSourceClientOwnsProofBearingCapabilityCall(t *testing.T) {
	request := attachmentsemantic.ResolveRequest{
		ParentObjectID: "WorkItem", ParentRecordID: "row-1", ResolverID: "attachment",
		Sources: []attachmentsemantic.Source{{SourceFieldID: "DocumentLink", SourceValue: "ABCDEF123456", SourceValueOffset: 9}},
	}
	var calls int
	client, err := NewAttachmentSourceClient(func(ctx context.Context, wireRequest *runtimev1.ResolveAttachmentSourcesRequest, options ...grpc.CallOption) (*runtimev1.ResolveAttachmentSourcesResponse, error) {
		calls++
		values, _ := metadata.FromOutgoingContext(ctx)
		assertExactly(t, values.Get(envelope.CapabilityMetadataKey), attachmentsemantic.CapabilityName)
		assertClientCallOptions(t, options)
		if wireRequest.GetParentObjectId() != request.ParentObjectID || len(wireRequest.GetSources()) != 1 ||
			wireRequest.GetSources()[0].GetSourceFieldId() != request.Sources[0].SourceFieldID ||
			wireRequest.GetSources()[0].GetSourceValueOffset() != request.Sources[0].SourceValueOffset {
			t.Fatalf("wire request = %#v", wireRequest)
		}
		return mapping.ToWireResolveAttachmentSourcesResponse(request, attachmentsemantic.ResolveResponse{
			Results:  []attachmentsemantic.SourceResult{{Index: 0, Outcome: attachmentsemantic.SourceResolved, FileID: "file-1", FileName: "spec.pdf"}},
			Receipts: attachmentsemantic.Receipts{ProtocolVersion: "1.0", CapabilityVersion: attachmentsemantic.CapabilityName},
		})
	})
	if err != nil {
		t.Fatal(err)
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	response, err := client.ResolveAttachmentSources(ctx, request)
	if err != nil || calls != 1 || len(response.Results) != 1 || response.Results[0].FileID != "file-1" {
		t.Fatalf("response=%#v error=%v calls=%d", response, err, calls)
	}
	if constructed, err := NewAttachmentSourceClient(nil); err == nil || constructed != nil {
		t.Fatalf("NewAttachmentSourceClient(nil) = %#v, %v", constructed, err)
	}
}

func TestIdentityExchangeClientKeepsBearerOutOfSemanticMessage(t *testing.T) {
	client, err := NewIdentityExchangeClient(func(ctx context.Context, request *runtimev1.ExchangeRequest, options ...grpc.CallOption) (*runtimev1.ExchangeResponse, error) {
		values, ok := metadata.FromOutgoingContext(ctx)
		if !ok {
			t.Fatal("outgoing metadata is missing")
		}
		assertExactly(t, values.Get(envelope.ProtocolMajorMetadataKey), "1")
		assertExactly(t, values.Get(envelope.ProtocolMinorMetadataKey), "0")
		assertExactly(t, values.Get(envelope.CapabilityMetadataKey), identitysemantic.CapabilityName)
		assertExactly(t, values.Get("authorization"), "Bearer workload-secret")
		assertExactly(t, values.Get(EndUserAuthorizationMetadataKey), "Bearer user-secret")
		assertClientCallOptions(t, options)
		if request.GetAppInstallationId() != "installation-1" || !reflect.DeepEqual(request.GetRequestedRuntimeCapabilities(), []string{"runtime.records.read-selected-root.v1"}) {
			t.Fatalf("wire request = %#v", request)
		}
		return &runtimev1.ExchangeResponse{OpaqueDelegation: "opaque.token.signature", ExpiresAtUnixSecond: 42}, nil
	})
	if err != nil {
		t.Fatal(err)
	}
	ctx, cancel := context.WithTimeout(metadata.NewOutgoingContext(context.Background(), metadata.Pairs(
		"authorization", "Bearer workload-secret",
	)), time.Second)
	defer cancel()
	response, err := client.Exchange(ctx, "user-secret", identitysemantic.Request{
		AppInstallationID: "installation-1", RequestedCapabilities: []string{"runtime.records.read-selected-root.v1"},
	})
	if err != nil || response.OpaqueDelegation != "opaque.token.signature" || response.ExpiresAtUnixSecond != 42 {
		t.Fatalf("response = %#v, %v", response, err)
	}
}

func TestIdentityExchangeClientRejectsMissingBearerBeforeRPC(t *testing.T) {
	var calls int
	client, err := NewIdentityExchangeClient(func(context.Context, *runtimev1.ExchangeRequest, ...grpc.CallOption) (*runtimev1.ExchangeResponse, error) {
		calls++
		return nil, nil
	})
	if err != nil {
		t.Fatal(err)
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	_, err = client.Exchange(ctx, "", identitysemantic.Request{AppInstallationID: "installation-1", RequestedCapabilities: []string{"runtime.records.read-selected-root.v1"}})
	var failure *identitysemantic.Failure
	if !errors.As(err, &failure) || failure.Category != identitysemantic.FailureUnauthenticated || calls != 0 {
		t.Fatalf("failure=%#v calls=%d", failure, calls)
	}
}

func assertExactly(t *testing.T, got []string, want string) {
	t.Helper()
	if len(got) != 1 || got[0] != want {
		t.Fatalf("metadata = %#v, want exactly %q", got, want)
	}
}

func assertClientCallOptions(t *testing.T, options []grpc.CallOption) {
	t.Helper()
	if len(options) != 3 {
		t.Fatalf("call options = %#v", options)
	}
	send, ok := options[0].(grpc.MaxSendMsgSizeCallOption)
	if !ok || send.MaxSendMsgSize != envelope.MaxRequestWireBytes {
		t.Fatalf("send option = %#v", options[0])
	}
	receive, ok := options[1].(grpc.MaxRecvMsgSizeCallOption)
	if !ok || receive.MaxRecvMsgSize != envelope.MaxResponseWireBytes {
		t.Fatalf("receive option = %#v", options[1])
	}
	failFast, ok := options[2].(grpc.FailFastCallOption)
	if !ok || !failFast.FailFast {
		t.Fatalf("wait-for-ready option = %#v", options[2])
	}
}
