Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,25 @@ func TestFoo(t *testing.T) {
}
```

`CREATE PROTO BUNDLE` and `ALTER PROTO BUNDLE` setup DDLs need serialized
descriptors alongside the DDL text. Pair [WithSetupDDLs] with
[WithSetupFileDescriptorSet] or [WithSetupRawFileDescriptorSet]:

```go
func TestProtoBundle(t *testing.T) {
fds := &descriptorpb.FileDescriptorSet{ /* ... */ }

env := spanemuboost.SetupEmulatorWithClients(t,
spanemuboost.WithSetupDDLs([]string{
"CREATE PROTO BUNDLE (`examples.shipping.Order`)",
}),
spanemuboost.WithSetupFileDescriptorSet(fds),
)

// env.Client is ready with the proto bundle applied
}
```

For non-test usage (e.g. embedding the emulator in an application where the `testing` package is unavailable), see runnable examples on [pkg.go.dev](https://pkg.go.dev/github.com/apstndb/spanemuboost#pkg-examples).

### Shared runtime, database-per-case
Expand Down
33 changes: 33 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"cloud.google.com/go/spanner/admin/database/apiv1/databasepb"

"github.com/apstndb/spanemuboost"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/descriptorpb"
)

func ExampleRunEmulatorWithClients() {
Expand All @@ -33,6 +35,37 @@ func ExampleRunEmulatorWithClients() {
// Output: {fields: [type:{code:INT64}], values: [string_value:"1"]}
}

func ExampleWithSetupFileDescriptorSet() {
ctx := context.Background()

fds := &descriptorpb.FileDescriptorSet{
File: []*descriptorpb.FileDescriptorProto{
{
Name: proto.String("shipping.proto"),
Syntax: proto.String("proto3"),
Package: proto.String("examples.shipping"),
MessageType: []*descriptorpb.DescriptorProto{
{Name: proto.String("Order")},
},
},
},
}

env, err := spanemuboost.RunEmulatorWithClients(ctx,
spanemuboost.WithSetupDDLs([]string{
"CREATE PROTO BUNDLE (`examples.shipping.Order`)",
}),
spanemuboost.WithSetupFileDescriptorSet(fds),
)
if err != nil {
log.Fatalln(err)
return
}
defer env.Close() //nolint:errcheck

_ = env.Client
}

func ExampleOpenClients() {
ctx := context.Background()

Expand Down
2 changes: 1 addition & 1 deletion options.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ func WithSetupDDLs(ddls []string) Option {
// WithSetupFileDescriptorSet sets proto descriptors for CREATE/ALTER PROTO BUNDLE
// statements in [WithSetupDDLs]. Use this option together with setup DDLs that
// reference proto bundles; the value is serialized for CreateDatabase and
// UpdateDatabaseDdl requests.
// UpdateDatabaseDdl requests at bootstrap time.
// Calling this multiple times replaces the previous value.
func WithSetupFileDescriptorSet(fds *descriptorpb.FileDescriptorSet) Option {
var raw []byte
Expand Down
120 changes: 120 additions & 0 deletions proto_bundle_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package spanemuboost

import (
"testing"

"cloud.google.com/go/spanner"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/descriptorpb"
)

func TestSetupEmulatorWithClientsProtoBundle(t *testing.T) {
env := SetupEmulatorWithClients(t,
WithSetupDDLs([]string{"CREATE PROTO BUNDLE (`examples.shipping.Order`)"}),
WithSetupFileDescriptorSet(exampleShippingFileDescriptorSet()),
)

ctx := t.Context()
var rows []struct {
SchemaName string `spanner:"SCHEMA_NAME"`
ProtoBundle []byte `spanner:"PROTO_BUNDLE"`
}
err := spanner.SelectAll(
env.Client.Single().Query(ctx, spanner.Statement{
SQL: "SELECT SCHEMA_NAME, PROTO_BUNDLE FROM INFORMATION_SCHEMA.SCHEMATA",
}),
&rows,
spanner.WithLenient(),
)
if err != nil {
t.Fatal(err)
}

var protoBundle []byte
for _, row := range rows {
if row.SchemaName == "" && len(row.ProtoBundle) > 0 {
protoBundle = row.ProtoBundle
break
}
}
if len(protoBundle) == 0 {
t.Fatal("SCHEMATA.PROTO_BUNDLE is empty for default schema")
}
}

func TestSetupClientsProtoBundleWithRawDescriptors(t *testing.T) {
raw, err := proto.Marshal(exampleShippingFileDescriptorSet())
if err != nil {
t.Fatalf("proto.Marshal: %v", err)
}

emu := SetupEmulator(t, EnableInstanceAutoConfigOnly())
clients := SetupClients(t, emu,
WithRandomDatabaseID(),
WithSetupDDLs([]string{"CREATE PROTO BUNDLE (`examples.shipping.Order`)"}),
WithSetupRawFileDescriptorSet(raw),
)

ctx := t.Context()
var rows []struct {
SchemaName string `spanner:"SCHEMA_NAME"`
ProtoBundle []byte `spanner:"PROTO_BUNDLE"`
}
err = spanner.SelectAll(
clients.Client.Single().Query(ctx, spanner.Statement{
SQL: "SELECT SCHEMA_NAME, PROTO_BUNDLE FROM INFORMATION_SCHEMA.SCHEMATA",
}),
&rows,
spanner.WithLenient(),
)
if err != nil {
t.Fatal(err)
}

var protoBundle []byte
for _, row := range rows {
if row.SchemaName == "" && len(row.ProtoBundle) > 0 {
protoBundle = row.ProtoBundle
break
}
}
if len(protoBundle) == 0 {
t.Fatal("SCHEMATA.PROTO_BUNDLE is empty for default schema")
}
}

func exampleShippingFileDescriptorSet() *descriptorpb.FileDescriptorSet {
return &descriptorpb.FileDescriptorSet{
File: []*descriptorpb.FileDescriptorProto{
{
Name: proto.String("shipping.proto"),
Syntax: proto.String("proto3"),
Package: proto.String("examples.shipping"),
EnumType: []*descriptorpb.EnumDescriptorProto{
{
Name: proto.String("ShippingSpeed"),
Value: []*descriptorpb.EnumValueDescriptorProto{
{Name: proto.String("SHIPPING_SPEED_UNSPECIFIED"), Number: proto.Int32(0)},
},
},
},
MessageType: []*descriptorpb.DescriptorProto{
{
Name: proto.String("Order"),
EnumType: []*descriptorpb.EnumDescriptorProto{
{
Name: proto.String("Status"),
Value: []*descriptorpb.EnumValueDescriptorProto{
{Name: proto.String("STATUS_UNSPECIFIED"), Number: proto.Int32(0)},
},
},
},
NestedType: []*descriptorpb.DescriptorProto{
{Name: proto.String("Address")},
},
},
},
},
},
}
}
Comment thread
apstndb marked this conversation as resolved.