package cloudcas

import (
	"bytes"
	"context"
	"crypto"
	"crypto/ecdsa"
	"crypto/ed25519"
	"crypto/rand"
	"crypto/x509"
	"encoding/asn1"
	"encoding/pem"
	"fmt"
	"io"
	"net"
	"os"
	"reflect"
	"testing"
	"time"

	lroauto "cloud.google.com/go/longrunning/autogen"
	privateca "cloud.google.com/go/security/privateca/apiv1"
	gomock "github.com/golang/mock/gomock"
	"github.com/google/uuid"
	gax "github.com/googleapis/gax-go/v2"
	"github.com/pkg/errors"
	"github.com/smallstep/certificates/cas/apiv1"
	kmsapi "github.com/smallstep/certificates/kms/apiv1"
	"google.golang.org/api/option"
	pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1"
	longrunningpb "google.golang.org/genproto/googleapis/longrunning"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/status"
	"google.golang.org/grpc/test/bufconn"
	"google.golang.org/protobuf/types/known/anypb"
)

var (
	errTest             = errors.New("test error")
	testCaPoolName      = "projects/test-project/locations/us-west1/caPools/test-capool"
	testAuthorityName   = "projects/test-project/locations/us-west1/caPools/test-capool/certificateAuthorities/test-ca"
	testCertificateName = "projects/test-project/locations/us-west1/caPools/test-capool/certificateAuthorities/test-ca/certificates/test-certificate"
	testProject         = "test-project"
	testLocation        = "us-west1"
	testCaPool          = "test-capool"
	testRootCertificate = `-----BEGIN CERTIFICATE-----
MIIBeDCCAR+gAwIBAgIQcXWWjtSZ/PAyH8D1Ou4L9jAKBggqhkjOPQQDAjAbMRkw
FwYDVQQDExBDbG91ZENBUyBSb290IENBMB4XDTIwMTAyNzIyNTM1NFoXDTMwMTAy
NzIyNTM1NFowGzEZMBcGA1UEAxMQQ2xvdWRDQVMgUm9vdCBDQTBZMBMGByqGSM49
AgEGCCqGSM49AwEHA0IABIySHA4b78Yu4LuGhZIlv/PhNwXz4ZoV1OUZQ0LrK3vj
B13O12DLZC5uj1z3kxdQzXUttSbtRv49clMpBiTpsZKjRTBDMA4GA1UdDwEB/wQE
AwIBBjASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBSZ+t9RMHbFTl5BatM3
5bJlHPOu3DAKBggqhkjOPQQDAgNHADBEAiASah6gg0tVM3WI0meCQ4SEKk7Mjhbv
+SmhuZHWV1QlXQIgRXNyWcpVUrAoG6Uy1KQg07LDpF5dFeK9InrDxSJAkVo=
-----END CERTIFICATE-----`
	testIntermediateCertificate = `-----BEGIN CERTIFICATE-----
MIIBpDCCAUmgAwIBAgIRALLKxnxyl0GBeKevIcbx02wwCgYIKoZIzj0EAwIwGzEZ
MBcGA1UEAxMQQ2xvdWRDQVMgUm9vdCBDQTAeFw0yMDEwMjcyMjUzNTRaFw0zMDEw
MjcyMjUzNTRaMCMxITAfBgNVBAMTGENsb3VkQ0FTIEludGVybWVkaWF0ZSBDQTBZ
MBMGByqGSM49AgEGCCqGSM49AwEHA0IABPLuqxgBY+QmaXc8zKIC8FMgjJ6dF/cL
b+Dig0XKc5GH/T1ORrhgOkRayrQcjPMu+jkjg25qn6vvp43LRtUKPXOjZjBkMA4G
A1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBQ8RVQI
VgXAmRNDX8qItalVpSBEGjAfBgNVHSMEGDAWgBSZ+t9RMHbFTl5BatM35bJlHPOu
3DAKBggqhkjOPQQDAgNJADBGAiEA70MVYVqjm8SBHJf5cOlWfiXXOfHUsctTJ+/F
pLsKBogCIQDJJkoQqYl9B59Dq3zydl8bpJevQxsoaa4Wqg+ZBMkvbQ==
-----END CERTIFICATE-----`
	testLeafCertificate = `-----BEGIN CERTIFICATE-----
MIIB1jCCAX2gAwIBAgIQQfOn+COMeuD8VYF1TiDkEzAKBggqhkjOPQQDAjAqMSgw
JgYDVQQDEx9Hb29nbGUgQ0FTIFRlc3QgSW50ZXJtZWRpYXRlIENBMB4XDTIwMDkx
NDIyNTE1NVoXDTMwMDkxMjIyNTE1MlowHTEbMBkGA1UEAxMSdGVzdC5zbWFsbHN0
ZXAuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAdUSRBrpgHFilN4eaGlN
nX2+xfjXa1Iwk2/+AensjFTXJi1UAIB0e+4pqi7Sen5E2QVBhntEHCrA3xOf7czg
P6OBkTCBjjAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG
AQUFBwMCMB0GA1UdDgQWBBSYPbu4Tmm7Zze/hCePeZH1Avoj+jAfBgNVHSMEGDAW
gBRIOVqyLDSlErJLuWWEvRm5UU1r1TAdBgNVHREEFjAUghJ0ZXN0LnNtYWxsc3Rl
cC5jb20wCgYIKoZIzj0EAwIDRwAwRAIgY+nTc+RHn31/BOhht4JpxCmJPHxqFT3S
ojnictBudV0CIB87ipY5HV3c8FLVEzTA0wFwdDZvQraQYsthwbg2kQFb
-----END CERTIFICATE-----`
	testSignedCertificate = `-----BEGIN CERTIFICATE-----
MIIB/DCCAaKgAwIBAgIQHHFuGMz0cClfde5kqP5prTAKBggqhkjOPQQDAjAqMSgw
JgYDVQQDEx9Hb29nbGUgQ0FTIFRlc3QgSW50ZXJtZWRpYXRlIENBMB4XDTIwMDkx
NTAwMDQ0M1oXDTMwMDkxMzAwMDQ0MFowHTEbMBkGA1UEAxMSdGVzdC5zbWFsbHN0
ZXAuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMqNCiXMvbn74LsHzRv+8
17m9vEzH6RHrg3m82e0uEc36+fZWV/zJ9SKuONmnl5VP79LsjL5SVH0RDj73U2XO
DKOBtjCBszAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG
AQUFBwMCMB0GA1UdDgQWBBRTA2cTs7PCNjnps/+T0dS8diqv0DAfBgNVHSMEGDAW
gBRIOVqyLDSlErJLuWWEvRm5UU1r1TBCBgwrBgEEAYKkZMYoQAIEMjAwEwhjbG91
ZGNhcxMkZDhkMThhNjgtNTI5Ni00YWYzLWFlNGItMmY4NzdkYTNmYmQ5MAoGCCqG
SM49BAMCA0gAMEUCIGxl+pqJ50WYWUqK2l4V1FHoXSi0Nht5kwTxFxnWZu1xAiEA
zemu3bhWLFaGg3s8i+HTEhw4RqkHP74vF7AVYp88bAw=
-----END CERTIFICATE-----`
	testIntermediateCsr = `-----BEGIN CERTIFICATE REQUEST-----
MIHeMIGFAgEAMCMxITAfBgNVBAMTGENsb3VkQ0FTIEludGVybWVkaWF0ZSBDQTBZ
MBMGByqGSM49AgEGCCqGSM49AwEHA0IABPLuqxgBY+QmaXc8zKIC8FMgjJ6dF/cL
b+Dig0XKc5GH/T1ORrhgOkRayrQcjPMu+jkjg25qn6vvp43LRtUKPXOgADAKBggq
hkjOPQQDAgNIADBFAiEAn3pkYXb2OzoQZ+AExFqd7qZ7pg2nyP2kBZZ01Pl8KfcC
IHKplBXDR79/i7kjOtv1iWfgf5S/XQHrz178gXA0YQe7
-----END CERTIFICATE REQUEST-----`
	testRootKey = `-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIN51Rgg6YcQVLeCRzumdw4pjM3VWqFIdCbnsV3Up1e/goAoGCCqGSM49
AwEHoUQDQgAEjJIcDhvvxi7gu4aFkiW/8+E3BfPhmhXU5RlDQusre+MHXc7XYMtk
Lm6PXPeTF1DNdS21Ju1G/j1yUykGJOmxkg==
-----END EC PRIVATE KEY-----`
	// nolint:unused,deadcode,gocritic
	testIntermediateKey = `-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIMMX/XkXGnRDD4fYu7Z4rHACdJn/iyOy2UTwsv+oZ0C+oAoGCCqGSM49
AwEHoUQDQgAE8u6rGAFj5CZpdzzMogLwUyCMnp0X9wtv4OKDRcpzkYf9PU5GuGA6
RFrKtByM8y76OSODbmqfq++njctG1Qo9cw==
-----END EC PRIVATE KEY-----`
)

type testClient struct {
	credentialsFile      string
	certificate          *pb.Certificate
	certificateAuthority *pb.CertificateAuthority
	err                  error
}

func newTestClient(credentialsFile string) (CertificateAuthorityClient, error) {
	if credentialsFile == "testdata/error.json" {
		return nil, errTest
	}
	return &testClient{
		credentialsFile: credentialsFile,
	}, nil
}

func okTestClient() *testClient {
	return &testClient{
		credentialsFile: "testdata/credentials.json",
		certificate: &pb.Certificate{
			Name:                testCertificateName,
			PemCertificate:      testSignedCertificate,
			PemCertificateChain: []string{testIntermediateCertificate, testRootCertificate},
		},
		certificateAuthority: &pb.CertificateAuthority{
			PemCaCertificates: []string{testIntermediateCertificate, testRootCertificate},
		},
	}
}

func failTestClient() *testClient {
	return &testClient{
		credentialsFile: "testdata/credentials.json",
		err:             errTest,
	}
}

func badTestClient() *testClient {
	return &testClient{
		credentialsFile: "testdata/credentials.json",
		certificate: &pb.Certificate{
			Name:                testCertificateName,
			PemCertificate:      "not a pem cert",
			PemCertificateChain: []string{testIntermediateCertificate, testRootCertificate},
		},
		certificateAuthority: &pb.CertificateAuthority{
			PemCaCertificates: []string{testIntermediateCertificate, "not a pem cert"},
		},
	}
}

func setTeeReader(t *testing.T, w *bytes.Buffer) {
	t.Helper()
	reader := rand.Reader
	t.Cleanup(func() {
		rand.Reader = reader
	})
	rand.Reader = io.TeeReader(reader, w)
}

type badSigner struct {
	pub crypto.PublicKey
}

func createBadSigner(t *testing.T) *badSigner {
	t.Helper()
	pub, _, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	return &badSigner{
		pub: pub,
	}
}

func (b *badSigner) Public() crypto.PublicKey {
	return b.pub
}

func (b *badSigner) Sign(rnd io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
	return nil, fmt.Errorf("💥")
}

func (c *testClient) CreateCertificate(ctx context.Context, req *pb.CreateCertificateRequest, opts ...gax.CallOption) (*pb.Certificate, error) {
	return c.certificate, c.err
}

func (c *testClient) RevokeCertificate(ctx context.Context, req *pb.RevokeCertificateRequest, opts ...gax.CallOption) (*pb.Certificate, error) {
	return c.certificate, c.err
}

func (c *testClient) GetCertificateAuthority(ctx context.Context, req *pb.GetCertificateAuthorityRequest, opts ...gax.CallOption) (*pb.CertificateAuthority, error) {
	return c.certificateAuthority, c.err
}

func (c *testClient) CreateCertificateAuthority(ctx context.Context, req *pb.CreateCertificateAuthorityRequest, opts ...gax.CallOption) (*privateca.CreateCertificateAuthorityOperation, error) {
	return nil, errors.New("use NewMockCertificateAuthorityClient")
}

func (c *testClient) FetchCertificateAuthorityCsr(ctx context.Context, req *pb.FetchCertificateAuthorityCsrRequest, opts ...gax.CallOption) (*pb.FetchCertificateAuthorityCsrResponse, error) {
	return nil, errors.New("use NewMockCertificateAuthorityClient")
}

func (c *testClient) ActivateCertificateAuthority(ctx context.Context, req *pb.ActivateCertificateAuthorityRequest, opts ...gax.CallOption) (*privateca.ActivateCertificateAuthorityOperation, error) {
	return nil, errors.New("use NewMockCertificateAuthorityClient")
}

func (c *testClient) EnableCertificateAuthority(ctx context.Context, req *pb.EnableCertificateAuthorityRequest, opts ...gax.CallOption) (*privateca.EnableCertificateAuthorityOperation, error) {
	return nil, errors.New("use NewMockCertificateAuthorityClient")
}

func (c *testClient) GetCaPool(ctx context.Context, req *pb.GetCaPoolRequest, opts ...gax.CallOption) (*pb.CaPool, error) {
	return nil, errors.New("use NewMockCertificateAuthorityClient")
}

func (c *testClient) CreateCaPool(ctx context.Context, req *pb.CreateCaPoolRequest, opts ...gax.CallOption) (*privateca.CreateCaPoolOperation, error) {
	return nil, errors.New("use NewMockCertificateAuthorityClient")
}

func mustParseCertificate(t *testing.T, pemCert string) *x509.Certificate {
	t.Helper()
	crt, err := parseCertificate(pemCert)
	if err != nil {
		t.Fatal(err)
	}
	return crt
}

func mustParseECKey(t *testing.T, pemKey string) *ecdsa.PrivateKey {
	t.Helper()
	block, _ := pem.Decode([]byte(pemKey))
	if block == nil {
		t.Fatal("failed to parse key")
	}
	key, err := x509.ParseECPrivateKey(block.Bytes)
	if err != nil {
		t.Fatal(err)
	}
	return key
}

func TestNew(t *testing.T) {
	tmp := newCertificateAuthorityClient
	newCertificateAuthorityClient = func(ctx context.Context, credentialsFile string) (CertificateAuthorityClient, error) {
		return newTestClient(credentialsFile)
	}
	t.Cleanup(func() {
		newCertificateAuthorityClient = tmp
	})

	type args struct {
		ctx  context.Context
		opts apiv1.Options
	}
	tests := []struct {
		name    string
		args    args
		want    *CloudCAS
		wantErr bool
	}{
		{"ok", args{context.Background(), apiv1.Options{
			CertificateAuthority: testAuthorityName,
		}}, &CloudCAS{
			client:               &testClient{},
			certificateAuthority: testAuthorityName,
			project:              testProject,
			location:             testLocation,
			caPool:               testCaPool,
			caPoolTier:           0,
		}, false},
		{"ok authority and creator", args{context.Background(), apiv1.Options{
			CertificateAuthority: testAuthorityName, IsCreator: true,
		}}, &CloudCAS{
			client:               &testClient{},
			certificateAuthority: testAuthorityName,
			project:              testProject,
			location:             testLocation,
			caPool:               testCaPool,
			caPoolTier:           0,
		}, false},
		{"ok with credentials", args{context.Background(), apiv1.Options{
			CertificateAuthority: testAuthorityName, CredentialsFile: "testdata/credentials.json",
		}}, &CloudCAS{
			client:               &testClient{credentialsFile: "testdata/credentials.json"},
			certificateAuthority: testAuthorityName,
			project:              testProject,
			location:             testLocation,
			caPool:               testCaPool,
			caPoolTier:           0,
		}, false},
		{"ok creator", args{context.Background(), apiv1.Options{
			IsCreator: true, Project: testProject, Location: testLocation, CaPool: testCaPool,
		}}, &CloudCAS{
			client:     &testClient{},
			project:    testProject,
			location:   testLocation,
			caPool:     testCaPool,
			caPoolTier: pb.CaPool_DEVOPS,
		}, false},
		{"ok creator devops", args{context.Background(), apiv1.Options{
			IsCreator: true, Project: testProject, Location: testLocation, CaPool: testCaPool, CaPoolTier: "DevOps",
		}}, &CloudCAS{
			client:     &testClient{},
			project:    testProject,
			location:   testLocation,
			caPool:     testCaPool,
			caPoolTier: pb.CaPool_DEVOPS,
		}, false},
		{"ok creator enterprise", args{context.Background(), apiv1.Options{
			IsCreator: true, Project: testProject, Location: testLocation, CaPool: testCaPool, CaPoolTier: "ENTERPRISE",
		}}, &CloudCAS{
			client:     &testClient{},
			project:    testProject,
			location:   testLocation,
			caPool:     testCaPool,
			caPoolTier: pb.CaPool_ENTERPRISE,
		}, false},
		{"fail certificate authority", args{context.Background(), apiv1.Options{
			CertificateAuthority: "projects/ok1234/locations/ok1234/caPools/ok1234/certificateAuthorities/ok1234/bad",
		}}, nil, true},
		{"fail certificate authority regex", args{context.Background(), apiv1.Options{}}, nil, true},
		{"fail with credentials", args{context.Background(), apiv1.Options{
			CertificateAuthority: testAuthorityName, CredentialsFile: "testdata/error.json",
		}}, nil, true},
		{"fail creator project", args{context.Background(), apiv1.Options{
			IsCreator: true, Project: "", Location: testLocation,
		}}, nil, true},
		{"fail creator location", args{context.Background(), apiv1.Options{
			IsCreator: true, Project: testProject, Location: "",
		}}, nil, true},
		{"fail caPool", args{context.Background(), apiv1.Options{
			IsCreator: true, Project: testProject, Location: testLocation, CaPool: "",
		}}, nil, true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := New(tt.args.ctx, tt.args.opts)
			if (err != nil) != tt.wantErr {
				t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("New() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestNew_register(t *testing.T) {
	tmp := newCertificateAuthorityClient
	newCertificateAuthorityClient = func(ctx context.Context, credentialsFile string) (CertificateAuthorityClient, error) {
		return newTestClient(credentialsFile)
	}
	t.Cleanup(func() {
		newCertificateAuthorityClient = tmp
	})

	want := &CloudCAS{
		client:               &testClient{credentialsFile: "testdata/credentials.json"},
		certificateAuthority: testAuthorityName,
		project:              testProject,
		location:             testLocation,
		caPool:               testCaPool,
	}

	newFn, ok := apiv1.LoadCertificateAuthorityServiceNewFunc(apiv1.CloudCAS)
	if !ok {
		t.Error("apiv1.LoadCertificateAuthorityServiceNewFunc(apiv1.CloudCAS) was not found")
		return
	}

	got, err := newFn(context.Background(), apiv1.Options{
		CertificateAuthority: testAuthorityName, CredentialsFile: "testdata/credentials.json",
	})
	if err != nil {
		t.Errorf("New() error = %v", err)
		return
	}
	if !reflect.DeepEqual(got, want) {
		t.Errorf("New() = %v, want %v", got, want)
	}
}

func TestNew_real(t *testing.T) {
	if v, ok := os.LookupEnv("GOOGLE_APPLICATION_CREDENTIALS"); ok {
		os.Unsetenv("GOOGLE_APPLICATION_CREDENTIALS")
		t.Cleanup(func() {
			os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", v)
		})
	}

	type args struct {
		ctx  context.Context
		opts apiv1.Options
	}
	tests := []struct {
		name     string
		skipOnCI bool
		args     args
		wantErr  bool
	}{
		{"fail default credentials", true, args{context.Background(), apiv1.Options{CertificateAuthority: testAuthorityName}}, true},
		{"fail certificate authority", false, args{context.Background(), apiv1.Options{}}, true},
		{"fail with credentials", false, args{context.Background(), apiv1.Options{
			CertificateAuthority: testAuthorityName, CredentialsFile: "testdata/missing.json",
		}}, true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.skipOnCI && os.Getenv("CI") == "true" {
				t.SkipNow()
			}
			_, err := New(tt.args.ctx, tt.args.opts)
			if (err != nil) != tt.wantErr {
				t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func TestCloudCAS_GetCertificateAuthority(t *testing.T) {
	root := mustParseCertificate(t, testRootCertificate)
	type fields struct {
		client               CertificateAuthorityClient
		certificateAuthority string
	}
	type args struct {
		req *apiv1.GetCertificateAuthorityRequest
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    *apiv1.GetCertificateAuthorityResponse
		wantErr bool
	}{
		{"ok", fields{okTestClient(), testCertificateName}, args{&apiv1.GetCertificateAuthorityRequest{}}, &apiv1.GetCertificateAuthorityResponse{
			RootCertificate: root,
		}, false},
		{"ok with name", fields{okTestClient(), testCertificateName}, args{&apiv1.GetCertificateAuthorityRequest{
			Name: testCertificateName,
		}}, &apiv1.GetCertificateAuthorityResponse{
			RootCertificate: root,
		}, false},
		{"fail GetCertificateAuthority", fields{failTestClient(), testCertificateName}, args{&apiv1.GetCertificateAuthorityRequest{}}, nil, true},
		{"fail bad root", fields{badTestClient(), testCertificateName}, args{&apiv1.GetCertificateAuthorityRequest{}}, nil, true},
		{"fail no pems", fields{&testClient{certificateAuthority: &pb.CertificateAuthority{}}, testCertificateName}, args{&apiv1.GetCertificateAuthorityRequest{}}, nil, true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			c := &CloudCAS{
				client:               tt.fields.client,
				certificateAuthority: tt.fields.certificateAuthority,
			}
			got, err := c.GetCertificateAuthority(tt.args.req)
			if (err != nil) != tt.wantErr {
				t.Errorf("CloudCAS.GetCertificateAuthority() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("CloudCAS.GetCertificateAuthority() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestCloudCAS_CreateCertificate(t *testing.T) {
	type fields struct {
		client               CertificateAuthorityClient
		certificateAuthority string
	}
	type args struct {
		req *apiv1.CreateCertificateRequest
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    *apiv1.CreateCertificateResponse
		wantErr bool
	}{
		{"ok", fields{okTestClient(), testCertificateName}, args{&apiv1.CreateCertificateRequest{
			Template: mustParseCertificate(t, testLeafCertificate),
			Lifetime: 24 * time.Hour,
		}}, &apiv1.CreateCertificateResponse{
			Certificate:      mustParseCertificate(t, testSignedCertificate),
			CertificateChain: []*x509.Certificate{mustParseCertificate(t, testIntermediateCertificate)},
		}, false},
		{"fail Template", fields{okTestClient(), testCertificateName}, args{&apiv1.CreateCertificateRequest{
			Lifetime: 24 * time.Hour,
		}}, nil, true},
		{"fail Lifetime", fields{okTestClient(), testCertificateName}, args{&apiv1.CreateCertificateRequest{
			Template: mustParseCertificate(t, testLeafCertificate),
		}}, nil, true},
		{"fail CreateCertificate", fields{failTestClient(), testCertificateName}, args{&apiv1.CreateCertificateRequest{
			Template: mustParseCertificate(t, testLeafCertificate),
			Lifetime: 24 * time.Hour,
		}}, nil, true},
		{"fail Certificate", fields{badTestClient(), testCertificateName}, args{&apiv1.CreateCertificateRequest{
			Template: mustParseCertificate(t, testLeafCertificate),
			Lifetime: 24 * time.Hour,
		}}, nil, true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			c := &CloudCAS{
				client:               tt.fields.client,
				certificateAuthority: tt.fields.certificateAuthority,
			}
			got, err := c.CreateCertificate(tt.args.req)
			if (err != nil) != tt.wantErr {
				t.Errorf("CloudCAS.CreateCertificate() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("CloudCAS.CreateCertificate() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestCloudCAS_createCertificate(t *testing.T) {
	leaf := mustParseCertificate(t, testLeafCertificate)
	signed := mustParseCertificate(t, testSignedCertificate)
	chain := []*x509.Certificate{mustParseCertificate(t, testIntermediateCertificate)}

	type fields struct {
		client               CertificateAuthorityClient
		certificateAuthority string
	}
	type args struct {
		tpl       *x509.Certificate
		lifetime  time.Duration
		requestID string
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    *x509.Certificate
		want1   []*x509.Certificate
		wantErr bool
	}{
		{"ok", fields{okTestClient(), testAuthorityName}, args{leaf, 24 * time.Hour, "request-id"}, signed, chain, false},
		{"fail CertificateConfig", fields{okTestClient(), testAuthorityName}, args{&x509.Certificate{}, 24 * time.Hour, "request-id"}, nil, nil, true},
		{"fail CreateCertificate", fields{failTestClient(), testAuthorityName}, args{leaf, 24 * time.Hour, "request-id"}, nil, nil, true},
		{"fail ParseCertificates", fields{badTestClient(), testAuthorityName}, args{leaf, 24 * time.Hour, "request-id"}, nil, nil, true},
		{"fail create id", fields{okTestClient(), testAuthorityName}, args{leaf, 24 * time.Hour, "request-id"}, nil, nil, true},
	}

	// Pre-calculate rand.Random
	buf := new(bytes.Buffer)
	setTeeReader(t, buf)
	for i := 0; i < len(tests)-1; i++ {
		_, err := uuid.NewRandomFromReader(rand.Reader)
		if err != nil {
			t.Fatal(err)
		}
	}
	rand.Reader = buf

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			c := &CloudCAS{
				client:               tt.fields.client,
				certificateAuthority: tt.fields.certificateAuthority,
			}
			got, got1, err := c.createCertificate(tt.args.tpl, tt.args.lifetime, tt.args.requestID)
			if (err != nil) != tt.wantErr {
				t.Errorf("CloudCAS.createCertificate() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("CloudCAS.createCertificate() got = %v, want %v", got, tt.want)
			}
			if !reflect.DeepEqual(got1, tt.want1) {
				t.Errorf("CloudCAS.createCertificate() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}

func TestCloudCAS_RenewCertificate(t *testing.T) {
	type fields struct {
		client               CertificateAuthorityClient
		certificateAuthority string
	}
	type args struct {
		req *apiv1.RenewCertificateRequest
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    *apiv1.RenewCertificateResponse
		wantErr bool
	}{
		{"ok", fields{okTestClient(), testCertificateName}, args{&apiv1.RenewCertificateRequest{
			Template: mustParseCertificate(t, testLeafCertificate),
			Lifetime: 24 * time.Hour,
		}}, &apiv1.RenewCertificateResponse{
			Certificate:      mustParseCertificate(t, testSignedCertificate),
			CertificateChain: []*x509.Certificate{mustParseCertificate(t, testIntermediateCertificate)},
		}, false},
		{"fail Template", fields{okTestClient(), testCertificateName}, args{&apiv1.RenewCertificateRequest{
			Lifetime: 24 * time.Hour,
		}}, nil, true},
		{"fail Lifetime", fields{okTestClient(), testCertificateName}, args{&apiv1.RenewCertificateRequest{
			Template: mustParseCertificate(t, testLeafCertificate),
		}}, nil, true},
		{"fail CreateCertificate", fields{failTestClient(), testCertificateName}, args{&apiv1.RenewCertificateRequest{
			Template: mustParseCertificate(t, testLeafCertificate),
			Lifetime: 24 * time.Hour,
		}}, nil, true},
		{"fail Certificate", fields{badTestClient(), testCertificateName}, args{&apiv1.RenewCertificateRequest{
			Template: mustParseCertificate(t, testLeafCertificate),
			Lifetime: 24 * time.Hour,
		}}, nil, true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			c := &CloudCAS{
				client:               tt.fields.client,
				certificateAuthority: tt.fields.certificateAuthority,
			}
			got, err := c.RenewCertificate(tt.args.req)
			if (err != nil) != tt.wantErr {
				t.Errorf("CloudCAS.RenewCertificate() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("CloudCAS.RenewCertificate() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestCloudCAS_RevokeCertificate(t *testing.T) {
	badExtensionCert := mustParseCertificate(t, testSignedCertificate)
	for i, ext := range badExtensionCert.Extensions {
		if ext.Id.Equal(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 37476, 9000, 64, 2}) {
			badExtensionCert.Extensions[i].Value = []byte("bad-data")
		}
	}

	type fields struct {
		client               CertificateAuthorityClient
		certificateAuthority string
	}
	type args struct {
		req *apiv1.RevokeCertificateRequest
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    *apiv1.RevokeCertificateResponse
		wantErr bool
	}{
		{"ok", fields{okTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
			Certificate: mustParseCertificate(t, testSignedCertificate),
			ReasonCode:  1,
		}}, &apiv1.RevokeCertificateResponse{
			Certificate:      mustParseCertificate(t, testSignedCertificate),
			CertificateChain: []*x509.Certificate{mustParseCertificate(t, testIntermediateCertificate)},
		}, false},
		{"fail Extension", fields{okTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
			Certificate: mustParseCertificate(t, testLeafCertificate),
			ReasonCode:  1,
		}}, nil, true},
		{"fail Extension Value", fields{okTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
			Certificate: badExtensionCert,
			ReasonCode:  1,
		}}, nil, true},
		{"fail Certificate", fields{okTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
			ReasonCode: 2,
		}}, nil, true},
		{"fail ReasonCode", fields{okTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
			Certificate: mustParseCertificate(t, testSignedCertificate),
			ReasonCode:  100,
		}}, nil, true},
		{"fail ReasonCode 7", fields{okTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
			Certificate: mustParseCertificate(t, testSignedCertificate),
			ReasonCode:  7,
		}}, nil, true},
		{"fail ReasonCode 8", fields{okTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
			Certificate: mustParseCertificate(t, testSignedCertificate),
			ReasonCode:  8,
		}}, nil, true},
		{"fail RevokeCertificate", fields{failTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
			Certificate: mustParseCertificate(t, testSignedCertificate),
			ReasonCode:  1,
		}}, nil, true},
		{"fail ParseCertificate", fields{badTestClient(), testCertificateName}, args{&apiv1.RevokeCertificateRequest{
			Certificate: mustParseCertificate(t, testSignedCertificate),
			ReasonCode:  1,
		}}, nil, true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			c := &CloudCAS{
				client:               tt.fields.client,
				certificateAuthority: tt.fields.certificateAuthority,
			}
			got, err := c.RevokeCertificate(tt.args.req)
			if (err != nil) != tt.wantErr {
				t.Errorf("CloudCAS.RevokeCertificate() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("CloudCAS.RevokeCertificate() = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_createCertificateID(t *testing.T) {
	buf := new(bytes.Buffer)
	setTeeReader(t, buf)
	id, err := uuid.NewRandomFromReader(rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	rand.Reader = buf

	tests := []struct {
		name    string
		want    string
		wantErr bool
	}{
		{"ok", id.String(), false},
		{"fail", "", true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := createCertificateID()
			if (err != nil) != tt.wantErr {
				t.Errorf("createCertificateID() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if got != tt.want {
				t.Errorf("createCertificateID() = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_parseCertificate(t *testing.T) {
	type args struct {
		pemCert string
	}
	tests := []struct {
		name    string
		args    args
		want    *x509.Certificate
		wantErr bool
	}{
		{"ok", args{testLeafCertificate}, mustParseCertificate(t, testLeafCertificate), false},
		{"ok intermediate", args{testIntermediateCertificate}, mustParseCertificate(t, testIntermediateCertificate), false},
		{"fail pem", args{"not pem"}, nil, true},
		{"fail parseCertificate", args{"-----BEGIN CERTIFICATE-----\nZm9vYmFyCg==\n-----END CERTIFICATE-----\n"}, nil, true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := parseCertificate(tt.args.pemCert)
			if (err != nil) != tt.wantErr {
				t.Errorf("parseCertificate() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("parseCertificate() = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_getCertificateAndChain(t *testing.T) {
	type args struct {
		certpb *pb.Certificate
	}
	tests := []struct {
		name    string
		args    args
		want    *x509.Certificate
		want1   []*x509.Certificate
		wantErr bool
	}{
		{"ok", args{&pb.Certificate{
			Name:                testCertificateName,
			PemCertificate:      testSignedCertificate,
			PemCertificateChain: []string{testIntermediateCertificate, testRootCertificate},
		}}, mustParseCertificate(t, testSignedCertificate), []*x509.Certificate{mustParseCertificate(t, testIntermediateCertificate)}, false},
		{"fail PemCertificate", args{&pb.Certificate{
			Name:                testCertificateName,
			PemCertificate:      "foobar",
			PemCertificateChain: []string{testIntermediateCertificate, testRootCertificate},
		}}, nil, nil, true},
		{"fail PemCertificateChain", args{&pb.Certificate{
			Name:                testCertificateName,
			PemCertificate:      testSignedCertificate,
			PemCertificateChain: []string{"foobar", testRootCertificate},
		}}, nil, nil, true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, got1, err := getCertificateAndChain(tt.args.certpb)
			if (err != nil) != tt.wantErr {
				t.Errorf("getCertificateAndChain() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("getCertificateAndChain() got = %v, want %v", got, tt.want)
			}
			if !reflect.DeepEqual(got1, tt.want1) {
				t.Errorf("getCertificateAndChain() got1 = %v, want %v", got1, tt.want1)
			}
		})
	}
}

func TestCloudCAS_CreateCertificateAuthority(t *testing.T) {
	must := func(a, b interface{}) interface{} {
		return a
	}

	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mosCtrl := gomock.NewController(t)
	defer mosCtrl.Finish()

	m := NewMockCertificateAuthorityClient(ctrl)
	mos := NewMockOperationsServer(mosCtrl)

	// Create operation server
	srv := grpc.NewServer()
	longrunningpb.RegisterOperationsServer(srv, mos)

	lis := bufconn.Listen(2)
	go srv.Serve(lis)
	defer srv.Stop()

	// Create fake privateca client
	conn, err := grpc.DialContext(context.Background(), "", grpc.WithTransportCredentials(insecure.NewCredentials()),
		grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) {
			return lis.Dial()
		}))
	if err != nil {
		t.Fatal(err)
	}

	client, err := lroauto.NewOperationsClient(context.Background(), option.WithGRPCConn(conn))
	if err != nil {
		t.Fatal(err)
	}
	fake, err := privateca.NewCertificateAuthorityClient(context.Background(), option.WithGRPCConn(conn))
	if err != nil {
		t.Fatal(err)
	}
	fake.LROClient = client

	// Configure mocks
	any := gomock.Any()

	// ok root
	m.EXPECT().GetCaPool(any, any).Return(nil, status.Error(codes.NotFound, "not found"))
	m.EXPECT().CreateCaPool(any, any).Return(fake.CreateCaPoolOperation("CreateCaPool"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "CreateCaPool",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CaPool{
				Name: testCaPoolName,
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "CreateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name:              testAuthorityName,
				PemCaCertificates: []string{testRootCertificate},
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().EnableCertificateAuthority(any, any).Return(fake.EnableCertificateAuthorityOperation("EnableCertificateAuthorityOperation"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "EnableCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name:              testAuthorityName,
				PemCaCertificates: []string{testRootCertificate},
			})).(*anypb.Any),
		},
	}, nil)

	// ok intermediate
	m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil)
	m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "CreateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name: testAuthorityName,
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().FetchCertificateAuthorityCsr(any, any).Return(&pb.FetchCertificateAuthorityCsrResponse{
		PemCsr: testIntermediateCsr,
	}, nil)
	m.EXPECT().CreateCertificate(any, any).Return(&pb.Certificate{
		PemCertificate:      testIntermediateCertificate,
		PemCertificateChain: []string{testRootCertificate},
	}, nil)
	m.EXPECT().ActivateCertificateAuthority(any, any).Return(fake.ActivateCertificateAuthorityOperation("ActivateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "ActivateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name:              testAuthorityName,
				PemCaCertificates: []string{testIntermediateCertificate, testRootCertificate},
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().EnableCertificateAuthority(any, any).Return(fake.EnableCertificateAuthorityOperation("EnableCertificateAuthorityOperation"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "EnableCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name:              testAuthorityName,
				PemCaCertificates: []string{testIntermediateCertificate, testRootCertificate},
			})).(*anypb.Any),
		},
	}, nil)

	// ok intermediate local signer
	m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil)
	m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "CreateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name: testAuthorityName,
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().FetchCertificateAuthorityCsr(any, any).Return(&pb.FetchCertificateAuthorityCsrResponse{
		PemCsr: testIntermediateCsr,
	}, nil)
	m.EXPECT().ActivateCertificateAuthority(any, any).Return(fake.ActivateCertificateAuthorityOperation("ActivateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "ActivateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name:              testAuthorityName,
				PemCaCertificates: []string{testIntermediateCertificate, testRootCertificate},
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().EnableCertificateAuthority(any, any).Return(fake.EnableCertificateAuthorityOperation("EnableCertificateAuthorityOperation"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "EnableCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name:              testAuthorityName,
				PemCaCertificates: []string{testIntermediateCertificate, testRootCertificate},
			})).(*anypb.Any),
		},
	}, nil)

	// ok create key
	m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil)
	m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "CreateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name:              testAuthorityName,
				PemCaCertificates: []string{testRootCertificate},
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().EnableCertificateAuthority(any, any).Return(fake.EnableCertificateAuthorityOperation("EnableCertificateAuthorityOperation"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "EnableCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name:              testAuthorityName,
				PemCaCertificates: []string{testRootCertificate},
			})).(*anypb.Any),
		},
	}, nil)

	// fail GetCaPool
	m.EXPECT().GetCaPool(any, any).Return(nil, errTest)

	// fail CreateCaPool
	m.EXPECT().GetCaPool(any, any).Return(nil, status.Error(codes.NotFound, "not found"))
	m.EXPECT().CreateCaPool(any, any).Return(nil, errTest)

	// fail CreateCaPool.Wait
	m.EXPECT().GetCaPool(any, any).Return(nil, status.Error(codes.NotFound, "not found"))
	m.EXPECT().CreateCaPool(any, any).Return(fake.CreateCaPoolOperation("CreateCaPool"), nil)
	mos.EXPECT().GetOperation(any, any).Return(nil, errTest)

	// fail CreateCertificateAuthority
	m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil)
	m.EXPECT().CreateCertificateAuthority(any, any).Return(nil, errTest)

	// fail CreateCertificateAuthority.Wait
	m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil)
	m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(nil, errTest)

	// fail EnableCertificateAuthority
	m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil)
	m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "CreateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name:              testAuthorityName,
				PemCaCertificates: []string{testRootCertificate},
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().EnableCertificateAuthority(any, any).Return(nil, errTest)

	// fail EnableCertificateAuthority.Wait
	m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil)
	m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "CreateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name:              testAuthorityName,
				PemCaCertificates: []string{testRootCertificate},
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().EnableCertificateAuthority(any, any).Return(fake.EnableCertificateAuthorityOperation("EnableCertificateAuthorityOperation"), nil)
	mos.EXPECT().GetOperation(any, any).Return(nil, errTest)

	// fail EnableCertificateAuthority intermediate
	m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil)
	m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "CreateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name: testAuthorityName,
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().FetchCertificateAuthorityCsr(any, any).Return(&pb.FetchCertificateAuthorityCsrResponse{
		PemCsr: testIntermediateCsr,
	}, nil)
	m.EXPECT().CreateCertificate(any, any).Return(&pb.Certificate{
		PemCertificate:      testIntermediateCertificate,
		PemCertificateChain: []string{testRootCertificate},
	}, nil)
	m.EXPECT().ActivateCertificateAuthority(any, any).Return(fake.ActivateCertificateAuthorityOperation("ActivateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "ActivateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name:              testAuthorityName,
				PemCaCertificates: []string{testIntermediateCertificate, testRootCertificate},
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().EnableCertificateAuthority(any, any).Return(nil, errTest)

	// fail EnableCertificateAuthority.Wait intermediate
	m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil)
	m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "CreateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name: testAuthorityName,
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().FetchCertificateAuthorityCsr(any, any).Return(&pb.FetchCertificateAuthorityCsrResponse{
		PemCsr: testIntermediateCsr,
	}, nil)
	m.EXPECT().CreateCertificate(any, any).Return(&pb.Certificate{
		PemCertificate:      testIntermediateCertificate,
		PemCertificateChain: []string{testRootCertificate},
	}, nil)
	m.EXPECT().ActivateCertificateAuthority(any, any).Return(fake.ActivateCertificateAuthorityOperation("ActivateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "ActivateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name:              testAuthorityName,
				PemCaCertificates: []string{testIntermediateCertificate, testRootCertificate},
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().EnableCertificateAuthority(any, any).Return(fake.EnableCertificateAuthorityOperation("EnableCertificateAuthorityOperation"), nil)
	mos.EXPECT().GetOperation(any, any).Return(nil, errTest)

	// fail FetchCertificateAuthorityCsr
	m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil)
	m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "CreateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name: testAuthorityName,
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().FetchCertificateAuthorityCsr(any, any).Return(nil, errTest)

	// fail CreateCertificate
	m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil)
	m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "CreateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name: testAuthorityName,
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().FetchCertificateAuthorityCsr(any, any).Return(&pb.FetchCertificateAuthorityCsrResponse{
		PemCsr: testIntermediateCsr,
	}, nil)
	m.EXPECT().CreateCertificate(any, any).Return(nil, errTest)

	// fail ActivateCertificateAuthority
	m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil)
	m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "CreateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name: testAuthorityName,
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().FetchCertificateAuthorityCsr(any, any).Return(&pb.FetchCertificateAuthorityCsrResponse{
		PemCsr: testIntermediateCsr,
	}, nil)
	m.EXPECT().CreateCertificate(any, any).Return(&pb.Certificate{
		PemCertificate:      testIntermediateCertificate,
		PemCertificateChain: []string{testRootCertificate},
	}, nil)
	m.EXPECT().ActivateCertificateAuthority(any, any).Return(nil, errTest)

	// fail ActivateCertificateAuthority.Wait
	m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil)
	m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "CreateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name: testAuthorityName,
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().FetchCertificateAuthorityCsr(any, any).Return(&pb.FetchCertificateAuthorityCsrResponse{
		PemCsr: testIntermediateCsr,
	}, nil)
	m.EXPECT().CreateCertificate(any, any).Return(&pb.Certificate{
		PemCertificate:      testIntermediateCertificate,
		PemCertificateChain: []string{testRootCertificate},
	}, nil)
	m.EXPECT().ActivateCertificateAuthority(any, any).Return(fake.ActivateCertificateAuthorityOperation("ActivateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(nil, errTest)

	// fail x509util.CreateCertificate
	m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil)
	m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "CreateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name: testAuthorityName,
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().FetchCertificateAuthorityCsr(any, any).Return(&pb.FetchCertificateAuthorityCsrResponse{
		PemCsr: testIntermediateCsr,
	}, nil)

	// fail parseCertificateRequest
	m.EXPECT().GetCaPool(any, any).Return(&pb.CaPool{Name: testCaPoolName}, nil)
	m.EXPECT().CreateCertificateAuthority(any, any).Return(fake.CreateCertificateAuthorityOperation("CreateCertificateAuthority"), nil)
	mos.EXPECT().GetOperation(any, any).Return(&longrunningpb.Operation{
		Name: "CreateCertificateAuthority",
		Done: true,
		Result: &longrunningpb.Operation_Response{
			Response: must(anypb.New(&pb.CertificateAuthority{
				Name: testAuthorityName,
			})).(*anypb.Any),
		},
	}, nil)
	m.EXPECT().FetchCertificateAuthorityCsr(any, any).Return(&pb.FetchCertificateAuthorityCsrResponse{
		PemCsr: "Not a CSR",
	}, nil)

	rootCrt := mustParseCertificate(t, testRootCertificate)
	intCrt := mustParseCertificate(t, testIntermediateCertificate)

	type fields struct {
		client               CertificateAuthorityClient
		certificateAuthority string
		project              string
		location             string
		caPool               string
		caPoolTier           pb.CaPool_Tier
	}
	type args struct {
		req *apiv1.CreateCertificateAuthorityRequest
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    *apiv1.CreateCertificateAuthorityResponse
		wantErr bool
	}{
		{"ok root", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_ENTERPRISE}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.RootCA,
			Template: mustParseCertificate(t, testRootCertificate),
			Lifetime: 24 * time.Hour,
		}}, &apiv1.CreateCertificateAuthorityResponse{
			Name:        testAuthorityName,
			Certificate: rootCrt,
		}, false},
		{"ok intermediate", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.IntermediateCA,
			Template: mustParseCertificate(t, testIntermediateCertificate),
			Lifetime: 24 * time.Hour,
			Parent: &apiv1.CreateCertificateAuthorityResponse{
				Name:        testAuthorityName,
				Certificate: rootCrt,
			},
		}}, &apiv1.CreateCertificateAuthorityResponse{
			Name:             testAuthorityName,
			Certificate:      intCrt,
			CertificateChain: []*x509.Certificate{rootCrt},
		}, false},
		{"ok intermediate local signer", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_ENTERPRISE}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.IntermediateCA,
			Template: mustParseCertificate(t, testIntermediateCertificate),
			Lifetime: 24 * time.Hour,
			Parent: &apiv1.CreateCertificateAuthorityResponse{
				Certificate: rootCrt,
				Signer:      mustParseECKey(t, testRootKey),
			},
		}}, &apiv1.CreateCertificateAuthorityResponse{
			Name:             testAuthorityName,
			Certificate:      intCrt,
			CertificateChain: []*x509.Certificate{rootCrt},
		}, false},
		{"ok create key", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.RootCA,
			Template: mustParseCertificate(t, testRootCertificate),
			Lifetime: 24 * time.Hour,
			CreateKey: &kmsapi.CreateKeyRequest{
				SignatureAlgorithm: kmsapi.ECDSAWithSHA256,
			},
		}}, &apiv1.CreateCertificateAuthorityResponse{
			Name:        testAuthorityName,
			Certificate: rootCrt,
		}, false},
		{"fail project", fields{m, "", "", testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.RootCA,
			Template: mustParseCertificate(t, testRootCertificate),
			Lifetime: 24 * time.Hour,
		}}, nil, true},
		{"fail location", fields{m, "", testProject, "", testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.RootCA,
			Template: mustParseCertificate(t, testRootCertificate),
			Lifetime: 24 * time.Hour,
		}}, nil, true},
		{"fail caPool", fields{m, "", testProject, testLocation, "", pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.RootCA,
			Template: mustParseCertificate(t, testRootCertificate),
			Lifetime: 24 * time.Hour,
		}}, nil, true},
		{"fail template", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.RootCA,
			Lifetime: 24 * time.Hour,
		}}, nil, true},
		{"fail lifetime", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.RootCA,
			Template: mustParseCertificate(t, testRootCertificate),
		}}, nil, true},
		{"fail parent", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.IntermediateCA,
			Template: mustParseCertificate(t, testRootCertificate),
			Lifetime: 24 * time.Hour,
		}}, nil, true},
		{"fail parent name", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.IntermediateCA,
			Template: mustParseCertificate(t, testRootCertificate),
			Lifetime: 24 * time.Hour,
			Parent:   &apiv1.CreateCertificateAuthorityResponse{},
		}}, nil, true},
		{"fail type", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     0,
			Template: mustParseCertificate(t, testRootCertificate),
			Lifetime: 24 * time.Hour,
		}}, nil, true},
		{"fail create key", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.RootCA,
			Template: mustParseCertificate(t, testRootCertificate),
			Lifetime: 24 * time.Hour,
			CreateKey: &kmsapi.CreateKeyRequest{
				SignatureAlgorithm: kmsapi.PureEd25519,
			},
		}}, nil, true},
		{"fail GetCaPool", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.RootCA,
			Template: mustParseCertificate(t, testRootCertificate),
			Lifetime: 24 * time.Hour,
		}}, nil, true},
		{"fail CreateCaPool", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.RootCA,
			Template: mustParseCertificate(t, testRootCertificate),
			Lifetime: 24 * time.Hour,
		}}, nil, true},
		{"fail CreateCaPool.Wait", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.RootCA,
			Template: mustParseCertificate(t, testRootCertificate),
			Lifetime: 24 * time.Hour,
		}}, nil, true},
		{"fail CreateCertificateAuthority", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.RootCA,
			Template: mustParseCertificate(t, testRootCertificate),
			Lifetime: 24 * time.Hour,
		}}, nil, true},
		{"fail CreateCertificateAuthority.Wait", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.RootCA,
			Template: mustParseCertificate(t, testRootCertificate),
			Lifetime: 24 * time.Hour,
		}}, nil, true},
		{"fail EnableCertificateAuthority", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.RootCA,
			Template: mustParseCertificate(t, testRootCertificate),
			Lifetime: 24 * time.Hour,
		}}, nil, true},
		{"fail EnableCertificateAuthority.Wait", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.RootCA,
			Template: mustParseCertificate(t, testRootCertificate),
			Lifetime: 24 * time.Hour,
		}}, nil, true},

		{"fail EnableCertificateAuthority intermediate", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.IntermediateCA,
			Template: mustParseCertificate(t, testIntermediateCertificate),
			Lifetime: 24 * time.Hour,
			Parent: &apiv1.CreateCertificateAuthorityResponse{
				Name:        testAuthorityName,
				Certificate: rootCrt,
			},
		}}, nil, true},
		{"fail EnableCertificateAuthority.Wait intermediate", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.IntermediateCA,
			Template: mustParseCertificate(t, testIntermediateCertificate),
			Lifetime: 24 * time.Hour,
			Parent: &apiv1.CreateCertificateAuthorityResponse{
				Name:        testAuthorityName,
				Certificate: rootCrt,
			},
		}}, nil, true},

		{"fail FetchCertificateAuthorityCsr", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.IntermediateCA,
			Template: mustParseCertificate(t, testIntermediateCertificate),
			Lifetime: 24 * time.Hour,
			Parent: &apiv1.CreateCertificateAuthorityResponse{
				Name:        testAuthorityName,
				Certificate: rootCrt,
			},
		}}, nil, true},
		{"fail CreateCertificate", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.IntermediateCA,
			Template: mustParseCertificate(t, testIntermediateCertificate),
			Lifetime: 24 * time.Hour,
			Parent: &apiv1.CreateCertificateAuthorityResponse{
				Name:        testAuthorityName,
				Certificate: rootCrt,
			},
		}}, nil, true},
		{"fail ActivateCertificateAuthority", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.IntermediateCA,
			Template: mustParseCertificate(t, testIntermediateCertificate),
			Lifetime: 24 * time.Hour,
			Parent: &apiv1.CreateCertificateAuthorityResponse{
				Name:        testAuthorityName,
				Certificate: rootCrt,
			},
		}}, nil, true},
		{"fail ActivateCertificateAuthority.Wait", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.IntermediateCA,
			Template: mustParseCertificate(t, testIntermediateCertificate),
			Lifetime: 24 * time.Hour,
			Parent: &apiv1.CreateCertificateAuthorityResponse{
				Name:        testAuthorityName,
				Certificate: rootCrt,
			},
		}}, nil, true},
		{"fail x509util.CreateCertificate", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.IntermediateCA,
			Template: mustParseCertificate(t, testIntermediateCertificate),
			Lifetime: 24 * time.Hour,
			Parent: &apiv1.CreateCertificateAuthorityResponse{
				Certificate: rootCrt,
				Signer:      createBadSigner(t),
			},
		}}, nil, true},
		{"fail parseCertificateRequest", fields{m, "", testProject, testLocation, testCaPool, pb.CaPool_DEVOPS}, args{&apiv1.CreateCertificateAuthorityRequest{
			Type:     apiv1.IntermediateCA,
			Template: mustParseCertificate(t, testIntermediateCertificate),
			Lifetime: 24 * time.Hour,
			Parent: &apiv1.CreateCertificateAuthorityResponse{
				Certificate: rootCrt,
				Signer:      createBadSigner(t),
			},
		}}, nil, true},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			c := &CloudCAS{
				client:               tt.fields.client,
				certificateAuthority: tt.fields.certificateAuthority,
				project:              tt.fields.project,
				location:             tt.fields.location,
				caPool:               tt.fields.caPool,
				caPoolTier:           tt.fields.caPoolTier,
			}
			got, err := c.CreateCertificateAuthority(tt.args.req)
			if (err != nil) != tt.wantErr {
				t.Errorf("CloudCAS.CreateCertificateAuthority() error = %+v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("CloudCAS.CreateCertificateAuthority() = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_normalizeCertificateAuthorityName(t *testing.T) {
	type args struct {
		name string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{"ok", args{"Test-CA-Name_1234"}, "Test-CA-Name_1234"},
		{"change", args{"💥 CA"}, "--CA"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := normalizeCertificateAuthorityName(tt.args.name); got != tt.want {
				t.Errorf("normalizeCertificateAuthorityName() = %v, want %v", got, tt.want)
			}
		})
	}
}
