googleapis / googleapis/google-cloud-go

spannertest: Inject In-Memory Listener for spannertest.NewServer

Open
#10,381 0 comments 1 reaction 1 assignee Claimed by @harshachinta View on GitHub
api: spanner priority: p3 triage me
Dominant language
Go
Stars
4.5k
Forks
1.6k
Avg merge
1d 13h
Merged PRs (30d)
109

Description

## spannertest: Inject In-Memory Listener for `spannertest.NewServer`

### Overview
The `spannertest.NewServer` currently opens a network port for communication. This behavior is not always desirable, especially in a testing environment where an in-memory listener would be more suitable. The inability to inject an in-memory listener limits the flexibility and usability of the `spannertest` package in isolated testing scenarios.

### Problem Statement
The `spannertest.NewServer` method initializes a server that listens on a specified TCP address. This design:
1. Necessitates the usage of network ports, which may not always be available or desirable in a controlled test environment.
2. Prevents the injection of a custom in-memory listener, which would allow for more flexible and efficient testing without relying on network resources.

### Proposed Solution
Modify the `spannertest.NewServer` method to accept an optional `net.Listener` parameter. This will allow the user to inject a custom listener (e.g., an in-memory listener) instead of always creating a new TCP listener.

### Detailed Design

1. **Modify the `NewServer` function signature:**
- Update the function to optionally accept a `net.Listener`. If no listener is provided, it will default to creating a TCP listener as it currently does.

```go
// NewServer creates a new Server.
// The Server will be listening for gRPC connections on the provided TCP address.
func NewServer(laddr string) (*Server, error) {
return NewServerWithListener(laddr, nil)
}

// NewServerWithListener creates a new Server with an optional custom listener.
// The Server will be listening for gRPC connections on the provided TCP address or custom listener.
// The resolved address is available in the Addr field.
func NewServerWithListener(laddr string, customListener net.Listener) (*Server, error) {
var l net.Listener
var err error

if customListener != nil {
l = customListener
} else {
l, err = net.Listen("tcp", laddr)
if err != nil {
return nil, err
}
}

s := &Server{
Addr: l.Addr().String(),
l: l,
srv: grpc.NewServer(),
s: &server{
logf: func(format string, args ...interface{}) {
log.Printf("spannertest.inmem: "+format, args...)
},
start: time.Now(),
sessions: make(map[string]*session),
lros: make(map[string]*lro),
},
}
adminpb.RegisterDatabaseAdminServer(s.srv, s.s)
spannerpb.RegisterSpannerServer(s.srv, s.s)
lropb.RegisterOperationsServer(s.srv, s.s)

go s.srv.Serve(s.l)

return s, nil
}

### Benefits
Flexibility: Allows for injection of custom listeners, enabling more controlled and isolated testing environments.
Efficiency: Avoids the need for network resources in tests, which can lead to faster and more reliable test runs.
Backward Compatibility: The proposed changes maintain backward compatibility by defaulting to the current behavior if no custom listener is provided.

### Conclusion
Enhancing the spannertest.NewServer method to support an optional in-memory listener will significantly improve its utility and flexibility in various testing scenarios. This change will align the functionality of spannertest with common testing best practices, facilitating more robust and efficient test suites.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.