Repository: ably/ably-go
Pull request #270 (2021-01-29 10:26:52)

Minor changes for 1.2 API compliance
Part of #271.


Patch:
diff --git a/ably/ablycrypto/crypto.go b/ably/ablycrypto/crypto.go
new file mode 100644
index 0000000..50bc06f
--- /dev/null
+++ b/ably/ablycrypto/crypto.go
@@ -0,0 +1,15 @@
+package ablycrypto
+
+import (
+	"github.com/ably/ably-go/ably/proto"
+)
+
+// GenerateRandomKey returns a random key. keyLength is optional; if non-zero,
+// it should be in bits.
+func GenerateRandomKey(keyLength int) ([]byte, error) {
+	var lens []int
+	if keyLength != 0 {
+		lens = append(lens, keyLength)
+	}
+	return proto.GenerateRandomKey(lens...)
+}
diff --git a/ably/auth.go b/ably/auth.go
index d52b28c..bf4d25f 100644
--- a/ably/auth.go
+++ b/ably/auth.go
@@ -430,7 +430,7 @@ func (a *Auth) requestAuthURL(params *TokenParams, opts *authOptions) (interface
 		var req TokenRequest
 		var buf bytes.Buffer
 		err := decode(typ, io.TeeReader(resp.Body, &buf), &req)
-		if err == nil && req.Mac != "" && req.Nonce != "" {
+		if err == nil && req.MAC != "" && req.Nonce != "" {
 			return &req, nil
 		}
 		var token TokenDetails
diff --git a/ably/options.go b/ably/options.go
index 95d459f..34a47b5 100644
--- a/ably/options.go
+++ b/ably/options.go
@@ -441,9 +441,9 @@ func AuthWithURL(url string) AuthOption {
 	}
 }
 
-func AuthWithMethod(url string) AuthOption {
+func AuthWithMethod(method string) AuthOption {
 	return func(os *authOptions) {
-		os.AuthMethod = url
+		os.AuthMethod = method
 	}
 }
 
@@ -501,9 +501,9 @@ func WithAuthURL(url string) ClientOption {
 	}
 }
 
-func WithAuthMethod(url string) ClientOption {
+func WithAuthMethod(method string) ClientOption {
 	return func(os *clientOptions) {
-		os.AuthMethod = url
+		os.AuthMethod = method
 	}
 }
 
@@ -519,6 +519,12 @@ func WithKey(key string) ClientOption {
 	}
 }
 
+func WithDefaultTokenParams(params TokenParams) ClientOption {
+	return func(os *clientOptions) {
+		os.DefaultTokenParams = &params
+	}
+}
+
 func WithQueryTime(queryTime bool) ClientOption {
 	return func(os *clientOptions) {
 		os.UseQueryTime = queryTime
@@ -662,11 +668,6 @@ func WithSuspendedRetryTimeout(d time.Duration) ClientOption {
 		os.SuspendedRetryTimeout = d
 	}
 }
-func WithConnectionStateTTL(d time.Duration) ClientOption {
-	return func(os *clientOptions) {
-		os.ConnectionStateTTL = d
-	}
-}
 
 func WithChannelRetryTimeout(d time.Duration) ClientOption {
 	return func(os *clientOptions) {
diff --git a/ably/paginated_result.go b/ably/paginated_result.go
index a236176..95fa615 100644
--- a/ably/paginated_result.go
+++ b/ably/paginated_result.go
@@ -210,7 +210,7 @@ func (p *PaginatedResult) Items() []interface{} {
 
 // Messages gives a slice of messages for the current page. The method panics if
 // the underlying paginated result is not a message.
-func (p *PaginatedResult) Messages() []*proto.Message {
+func (p *PaginatedResult) Messages() []*Message {
 	items, ok := p.typItems.([]*proto.Message)
 	if !ok {
 		panic(errInvalidType{typ: p.req.typ})
@@ -220,7 +220,7 @@ func (p *PaginatedResult) Messages() []*proto.Message {
 
 // PresenceMessages gives a slice of presence messages for the current path.
 // The method panics if the underlying paginated result is not a presence message.
-func (p *PaginatedResult) PresenceMessages() []*proto.PresenceMessage {
+func (p *PaginatedResult) PresenceMessages() []*PresenceMessage {
 	items, ok := p.typItems.([]*proto.PresenceMessage)
 	if !ok {
 		panic(errInvalidType{typ: p.req.typ})
@@ -230,7 +230,7 @@ func (p *PaginatedResult) PresenceMessages() []*proto.PresenceMessage {
 
 // Stats gives a slice of statistics for the current page. The method panics if
 // the underlying paginated result is not statistics.
-func (p *PaginatedResult) Stats() []*proto.Stats {
+func (p *PaginatedResult) Stats() []*Stats {
 	items, ok := p.typItems.([]*proto.Stats)
 	if !ok {
 		panic(errInvalidType{typ: p.req.typ})
@@ -238,6 +238,16 @@ func (p *PaginatedResult) Stats() []*proto.Stats {
 	return items
 }
 
+type Stats = proto.Stats
+type StatsMessageTypes = proto.MessageTypes
+type StatsMessageTraffic = proto.MessageTraffic
+type StatsConnectionTypes = proto.ConnectionTypes
+type StatsResourceCount = proto.ResourceCount
+type StatsRequestCount = proto.RequestCount
+type StatsPushStats = proto.PushStats
+type StatsXchgMessages = proto.XchgMessages
+type PushStats = proto.PushStats
+
 func (c *PaginatedResult) buildPaginatedPath(path string, params *PaginateParams) (string, error) {
 	if params == nil {
 		return path, nil
diff --git a/ably/realtime_channel.go b/ably/realtime_channel.go
index 9c1945c..136756b 100644
--- a/ably/realtime_channel.go
+++ b/ably/realtime_channel.go
@@ -22,16 +22,16 @@ func (ch chanSlice) Less(i, j int) bool { return ch[i].Name < ch[j].Name }
 func (ch chanSlice) Swap(i, j int)      { ch[i], ch[j] = ch[j], ch[i] }
 func (ch chanSlice) Sort()              { sort.Sort(ch) }
 
-// Channels is a goroutine-safe container for realtime channels that allows
+// RealtimeChannels is a goroutine-safe container for realtime channels that allows
 // for creating, deleting and iterating over existing channels.
-type Channels struct {
+type RealtimeChannels struct {
 	mtx    sync.Mutex
 	client *Realtime
 	chans  map[string]*RealtimeChannel
 }
 
-func newChannels(client *Realtime) *Channels {
-	return &Channels{
+func newChannels(client *Realtime) *RealtimeChannels {
+	return &RealtimeChannels{
 		client: client,
 		chans:  make(map[string]*RealtimeChannel),
 	}
@@ -57,19 +57,23 @@ func ChannelWithCipherKey(key []byte) ChannelOption {
 }
 
 // Cipher sets cipher parameters for encrypting messages on a channel.
-func ChannelWithCipher(params proto.CipherParams) ChannelOption {
+func ChannelWithCipher(params CipherParams) ChannelOption {
 	return func(o *channelOptions) {
 		o.Cipher = params
 	}
 }
 
+type CipherParams = proto.CipherParams
+type CipherAlgorith = proto.CipherAlgorithm
+type CipherMode = proto.CipherMode
+
 // Get looks up a channel given by the name and creates it if it does not exist
 // already.
 //
 // It is safe to call Get from multiple goroutines - a single channel is
 // guaranteed to be created only once for multiple calls to Get from different
 // goroutines.
-func (ch *Channels) Get(name string, options ...ChannelOption) *RealtimeChannel {
+func (ch *RealtimeChannels) Get(name string, options ...ChannelOption) *RealtimeChannel {
 	// TODO: options
 	ch.mtx.Lock()
 	c, ok := ch.chans[name]
@@ -88,7 +92,7 @@ func (ch *Channels) Get(name string, options ...ChannelOption) *RealtimeChannel
 // different goroutine.
 //
 // The returned list is sorted by channel names.
-func (ch *Channels) All() []*RealtimeChannel {
+func (ch *RealtimeChannels) All() []*RealtimeChannel {
 	ch.mtx.Lock()
 	chans := make([]*RealtimeChannel, 0, len(ch.chans))
 	for _, c := range ch.chans {
@@ -99,9 +103,17 @@ func (ch *Channels) All() []*RealtimeChannel {
 	return chans
 }
 
+// Exists returns true if the channel by the given name exists.
+func (c *RealtimeChannels) Exists(name string) bool {
+	c.mtx.Lock()
+	_, ok := c.chans[name]
+	c.mtx.Unlock()
+	return ok
+}
+
 // Release releases all resources associated with a channel, detaching it first
 // if necessary. See RealtimeChannel.Detach for details.
-func (ch *Channels) Release(ctx context.Context, name string) error {
+func (ch *RealtimeChannels) Release(ctx context.Context, name string) error {
 	ch.mtx.Lock()
 	defer ch.mtx.Unlock()
 	c, ok := ch.chans[name]
@@ -116,7 +128,7 @@ func (ch *Channels) Release(ctx context.Context, name string) error {
 	return nil
 }
 
-func (ch *Channels) broadcastConnStateChange(change ConnectionStateChange) {
+func (ch *RealtimeChannels) broadcastConnStateChange(change ConnectionStateChange) {
 	ch.mtx.Lock()
 	defer ch.mtx.Unlock()
 	for _, c := range ch.chans {
@@ -181,11 +193,11 @@ func (c *RealtimeChannel) Attach(ctx context.Context) error {
 	return res.Wait(ctx)
 }
 
-func (c *RealtimeChannel) attach() (Result, error) {
+func (c *RealtimeChannel) attach() (result, error) {
 	return c.mayAttach(true)
 }
 
-func (c *RealtimeChannel) mayAttach(checkActive bool) (Result, error) {
+func (c *RealtimeChannel) mayAttach(checkActive bool) (result, error) {
 	c.mtx.Lock()
 	defer c.mtx.Unlock()
 	if checkActive {
@@ -199,7 +211,7 @@ func (c *RealtimeChannel) mayAttach(checkActive bool) (Result, error) {
 	return c.lockAttach(nil)
 }
 
-func (c *RealtimeChannel) lockAttach(err error) (Result, error) {
+func (c *RealtimeChannel) lockAttach(err error) (result, error) {
 	switch c.client.Connection.State() {
 	// RTL4b
 	case ConnectionStateInitialized,
@@ -235,7 +247,7 @@ func (c *RealtimeChannel) Detach(ctx context.Context) error {
 	return res.Wait(ctx)
 }
 
-func (c *RealtimeChannel) detach() (Result, error) {
+func (c *RealtimeChannel) detach() (result, error) {
 	c.mtx.Lock()
 	defer c.mtx.Unlock()
 	if !c.isActive() {
@@ -244,13 +256,13 @@ func (c *RealtimeChannel) detach() (Result, error) {
 	return c.detachUnsafe()
 }
 
-func (c *RealtimeChannel) detachSkipVerifyActive() (Result, error) {
+func (c *RealtimeChannel) detachSkipVerifyActive() (result, error) {
 	c.mtx.Lock()
 	defer c.mtx.Unlock()
 	return c.detachUnsafe()
 }
 
-func (c *RealtimeChannel) detachUnsafe() (Result, error) {
+func (c *RealtimeChannel) detachUnsafe() (result, error) {
 	if c.state == ChannelStateFailed {
 		return nil, channelStateError(ChannelStateFailed, errDetach)
 	}
@@ -426,7 +438,7 @@ func (c *RealtimeChannel) History(params *PaginateParams) (*PaginatedResult, err
 	return c.client.rest.Channels.Get(c.Name).History(params)
 }
 
-func (c *RealtimeChannel) send(msg *proto.ProtocolMessage) (Result, error) {
+func (c *RealtimeChannel) send(msg *proto.ProtocolMessage) (result, error) {
 	if res, enqueued := c.maybeEnqueue(msg); enqueued {
 		return res, nil
 	}
@@ -440,7 +452,7 @@ func (c *RealtimeChannel) send(msg *proto.ProtocolMessage) (Result, error) {
 	return res, nil
 }
 
-func (c *RealtimeChannel) maybeEnqueue(msg *proto.ProtocolMessage) (_ Result, enqueued bool) {
+func (c *RealtimeChannel) maybeEnqueue(msg *proto.ProtocolMessage) (_ result, enqueued bool) {
 	// RTL6c2
 	if c.opts().NoQueueing {
 		return nil, false
@@ -491,8 +503,8 @@ func (c *RealtimeChannel) State() ChannelState {
 	return c.state
 }
 
-// Reason gives the last error that caused channel transition to failed state.
-func (c *RealtimeChannel) Reason() *ErrorInfo {
+// ErrorReason gives the last error that caused channel transition to failed state.
+func (c *RealtimeChannel) ErrorReason() *ErrorInfo {
 	c.mtx.Lock()
 	defer c.mtx.Unlock()
 	return c.errorReason
@@ -514,7 +526,7 @@ func (c *RealtimeChannel) notify(msg *proto.ProtocolMessage) {
 			c.mtx.Unlock()
 			return
 		case ChannelStateAttached: // TODO: Also SUSPENDED; RTL13a
-			var res Result
+			var res result
 			res, err = c.lockAttach(err)
 			if err != nil {
 				break
@@ -628,7 +640,7 @@ func (c *RealtimeChannel) lockSetState(state ChannelState, err error) error {
 		Reason:   c.errorReason,
 	}
 	if !changed {
-		change.Event = ChannelEventUpdated
+		change.Event = ChannelEventUpdate
 	} else {
 		change.Event = ChannelEvent(change.Current)
 	}
diff --git a/ably/realtime_client.go b/ably/realtime_client.go
index 715897c..d143738 100644
--- a/ably/realtime_client.go
+++ b/ably/realtime_client.go
@@ -12,7 +12,7 @@ import (
 // state.
 type Realtime struct {
 	Auth       *Auth
-	Channels   *Channels
+	Channels   *RealtimeChannels
 	Connection *Connection
 
 	rest *REST
diff --git a/ably/realtime_conn.go b/ably/realtime_conn.go
index 5ed47f6..f52c25e 100644
--- a/ably/realtime_conn.go
+++ b/ably/realtime_conn.go
@@ -129,7 +129,7 @@ func (c *Connection) dial(proto string, u *url.URL) (conn proto.Conn, err error)
 	return conn, err
 }
 
-func (c *Connection) connectAfterSuspension(arg connArgs) (Result, error) {
+func (c *Connection) connectAfterSuspension(arg connArgs) (result, error) {
 	retryIn := c.opts.suspendedRetryTimeout()
 	lg := c.logger().sugar()
 	lg.Debugf("Attemting to periodically establish connection after suspension with timeout %v", retryIn)
@@ -187,7 +187,7 @@ func (c *Connection) Close() {
 	c.close()
 }
 
-func (c *Connection) connect(arg connArgs) (Result, error) {
+func (c *Connection) connect(arg connArgs) (result, error) {
 	c.mtx.Lock()
 	arg.mode = c.getMode()
 	c.mtx.Unlock()
@@ -203,7 +203,7 @@ type connArgs struct {
 	retryIn        time.Duration
 }
 
-func (c *Connection) reconnect(arg connArgs) (Result, error) {
+func (c *Connection) reconnect(arg connArgs) (result, error) {
 	c.mtx.Lock()
 
 	var mode connectionMode
@@ -287,7 +287,7 @@ func (c *Connection) params(mode connectionMode) (url.Values, error) {
 
 const connectionStateTTLErrFmt = "Exceeded connectionStateTtl=%v while in DISCONNECTED state"
 
-func (c *Connection) connectWithRetryLoop(arg connArgs) (Result, error) {
+func (c *Connection) connectWithRetryLoop(arg connArgs) (result, error) {
 	lg := c.logger().sugar()
 	res, err := c.connectWith(arg)
 	if err == nil {
@@ -368,7 +368,7 @@ loop:
 	return c.connectAfterSuspension(arg)
 }
 
-func (c *Connection) connectWith(arg connArgs) (Result, error) {
+func (c *Connection) connectWith(arg connArgs) (result, error) {
 	c.mtx.Lock()
 	if !c.isActive() {
 		c.lockSetState(ConnectionStateConnecting, nil, 0)
@@ -379,7 +379,7 @@ func (c *Connection) connectWith(arg connArgs) (Result, error) {
 	if err != nil {
 		return nil, err
 	}
-	var res Result
+	var res result
 	if arg.result {
 		res = c.internalEmitter.listenResult(
 			ConnectionStateConnected, // expected state
@@ -940,7 +940,7 @@ func (c *Connection) lockSetState(state ConnectionState, err error, retryIn time
 		RetryIn:  retryIn,
 	}
 	if !changed {
-		change.Event = ConnectionEventUpdated
+		change.Event = ConnectionEventUpdate
 	} else {
 		change.Event = ConnectionEvent(change.Current)
 	}
diff --git a/ably/realtime_presence.go b/ably/realtime_presence.go
index 63a3063..b98e927 100644
--- a/ably/realtime_presence.go
+++ b/ably/realtime_presence.go
@@ -56,7 +56,7 @@ func (pres *RealtimePresence) verifyChanState() error {
 	}
 }
 
-func (pres *RealtimePresence) send(msg *proto.PresenceMessage) (Result, error) {
+func (pres *RealtimePresence) send(msg *proto.PresenceMessage) (result, error) {
 	attached, err := pres.channel.attach()
 	if err != nil {
 		return nil, err
@@ -211,7 +211,7 @@ func (pres *RealtimePresence) processIncomingMessage(msg *proto.ProtocolMessage,
 // If the context is canceled before the operation finishes, the call
 // returns with an error, but the operation carries on in the background and
 // the channel may eventually be attached anyway.
-func (pres *RealtimePresence) Get(ctx context.Context) ([]*proto.PresenceMessage, error) {
+func (pres *RealtimePresence) Get(ctx context.Context) ([]*PresenceMessage, error) {
 	return pres.GetWithOptions(ctx)
 }
 
@@ -240,7 +240,7 @@ func (o *presenceGetOptions) applyWithDefaults(options ...PresenceGetOption) {
 }
 
 // GetWithOptions is Get with optional parameters.
-func (pres *RealtimePresence) GetWithOptions(ctx context.Context, options ...PresenceGetOption) ([]*proto.PresenceMessage, error) {
+func (pres *RealtimePresence) GetWithOptions(ctx context.Context, options ...PresenceGetOption) ([]*PresenceMessage, error) {
 	var opts presenceGetOptions
 	opts.applyWithDefaults(options...)
 
diff --git a/ably/rest_client.go b/ably/rest_client.go
index ff36082..571de8b 100644
--- a/ably/rest_client.go
+++ b/ably/rest_client.go
@@ -106,20 +106,12 @@ func (c *RESTChannels) get(name string, opts *proto.ChannelOptions) *RESTChannel
 }
 
 // Release deletes the channel from the cache.
-func (c *RESTChannels) Release(ch *RESTChannel) {
+func (c *RESTChannels) Release(name string) {
 	c.mu.Lock()
-	delete(c.cache, ch.Name)
+	delete(c.cache, name)
 	c.mu.Unlock()
 }
 
-// Len returns the number of channels stored.
-func (c *RESTChannels) Len() (size int) {
-	c.mu.RLock()
-	size = len(c.cache)
-	c.mu.RUnlock()
-	return
-}
-
 type REST struct {
 	Auth                *Auth
 	Channels            *RESTChannels
diff --git a/ably/state.go b/ably/state.go
index ea4f963..19f0c0f 100644
--- a/ably/state.go
+++ b/ably/state.go
@@ -10,16 +10,16 @@ import (
 	"github.com/ably/ably-go/ably/proto"
 )
 
-// Result awaits completion of asynchronous operation.
-type Result interface {
+// result awaits completion of asynchronous operation.
+type result interface {
 	// Wait blocks until asynchronous operation is completed. Upon its completion,
 	// the method returns nil error if it was successful and non-nil error otherwise.
 	// It's allowed to call Wait multiple times.
 	Wait(context.Context) error
 }
 
-func wait(ctx context.Context) func(Result, error) error {
-	return func(res Result, err error) error {
+func wait(ctx context.Context) func(result, error) error {
+	return func(res result, err error) error {
 		if err != nil {
 			return err
 		}
@@ -29,7 +29,7 @@ func wait(ctx context.Context) func(Result, error) error {
 
 // goWaiter immediately calls the given function in a separate goroutine. The
 // returned Result waits for its completion and returns its error.
-func goWaiter(f func() error) Result {
+func goWaiter(f func() error) result {
 	err := make(chan error, 1)
 	go func() {
 		defer close(err)
@@ -253,7 +253,7 @@ type errResult struct {
 	listen <-chan error
 }
 
-func newErrResult() (Result, chan<- error) {
+func newErrResult() (result, chan<- error) {
 	listen := make(chan error, 1)
 	res := &errResult{listen: listen}
 	return res, listen
@@ -281,7 +281,7 @@ func (f resultFunc) Wait(ctx context.Context) error {
 	return f(ctx)
 }
 
-func (e ChannelEventEmitter) listenResult(expected ChannelState, failed ...ChannelState) Result {
+func (e ChannelEventEmitter) listenResult(expected ChannelState, failed ...ChannelState) result {
 	// Make enough room not to block the sender if the Result is never waited on.
 	changes := make(channelStateChanges, 1+len(failed))
 
@@ -318,7 +318,7 @@ func (e ChannelEventEmitter) listenResult(expected ChannelState, failed ...Chann
 	})
 }
 
-func (e ConnectionEventEmitter) listenResult(expected ConnectionState, failed ...ConnectionState) Result {
+func (e ConnectionEventEmitter) listenResult(expected ConnectionState, failed ...ConnectionState) result {
 	// Make enough room not to block the sender if the Result is never waited on.
 	changes := make(connStateChanges, 1+len(failed))
 
@@ -392,7 +392,7 @@ var (
 	ConnectionEventClosing      ConnectionEvent = ConnectionEvent(ConnectionStateClosing)
 	ConnectionEventClosed       ConnectionEvent = ConnectionEvent(ConnectionStateClosed)
 	ConnectionEventFailed       ConnectionEvent = ConnectionEvent(ConnectionStateFailed)
-	ConnectionEventUpdated      ConnectionEvent = ConnectionEvent{name: "UPDATED"}
+	ConnectionEventUpdate       ConnectionEvent = ConnectionEvent{name: "UPDATE"}
 )
 
 func (e ConnectionEvent) String() string {
@@ -450,7 +450,7 @@ var (
 	ChannelEventDetached    ChannelEvent = ChannelEvent(ChannelStateDetached)
 	ChannelEventSuspended   ChannelEvent = ChannelEvent(ChannelStateSuspended)
 	ChannelEventFailed      ChannelEvent = ChannelEvent(ChannelStateFailed)
-	ChannelEventUpdated     ChannelEvent = ChannelEvent{name: "UPDATED"}
+	ChannelEventUpdate      ChannelEvent = ChannelEvent{name: "UPDATE"}
 )
 
 func (e ChannelEvent) String() string {
diff --git a/ably/token.go b/ably/token.go
index 3e9458c..ba393ff 100644
--- a/ably/token.go
+++ b/ably/token.go
@@ -57,7 +57,7 @@ type TokenRequest struct {
 
 	KeyName string `json:"keyName,omitempty" codec:"keyName,omitempty"`
 	Nonce   string `json:"nonce,omitempty" codec:"nonce,omitempty"` // should be at least 16 characters long
-	Mac     string `json:"mac,omitempty" codec:"mac,omitempty"`     // message authentication code for the request
+	MAC     string `json:"mac,omitempty" codec:"mac,omitempty"`     // message authentication code for the request
 }
 
 func (TokenRequest) IsTokener() {}
@@ -71,7 +71,7 @@ func (req *TokenRequest) sign(secret []byte) {
 	fmt.Fprintln(mac, req.ClientID)
 	fmt.Fprintln(mac, req.Timestamp)
 	fmt.Fprintln(mac, req.Nonce)
-	req.Mac = base64.StdEncoding.EncodeToString(mac.Sum(nil))
+	req.MAC = base64.StdEncoding.EncodeToString(mac.Sum(nil))
 }
 
 // TokenDetails


Test patch:
diff --git a/ably/ably_test.go b/ably/ably_test.go
index 8ea6159..010ec23 100644
--- a/ably/ably_test.go
+++ b/ably/ably_test.go
@@ -1,6 +1,7 @@
 package ably_test
 
 import (
+	"context"
 	"fmt"
 	"io"
 	"net/http"
@@ -12,6 +13,10 @@ import (
 	"github.com/ably/ably-go/ably/proto"
 )
 
+type Result interface {
+	Wait(context.Context) error
+}
+
 func nonil(err ...error) error {
 	for _, err := range err {
 		if err != nil {
diff --git a/ably/ablytest/resultgroup.go b/ably/ablytest/resultgroup.go
index b8e34cf..694a62e 100644
--- a/ably/ablytest/resultgroup.go
+++ b/ably/ablytest/resultgroup.go
@@ -10,7 +10,11 @@ import (
 	"github.com/ably/ably-go/ably"
 )
 
-func Wait(res ably.Result, err error) error {
+type Result interface {
+	Wait(context.Context) error
+}
+
+func Wait(res Result, err error) error {
 	if err != nil {
 		return err
 	}
@@ -26,11 +30,11 @@ func Wait(res ably.Result, err error) error {
 	}
 }
 
-// ResultGroup is like sync.WaitGroup, but for ably.Result values.
+// ResultGroup is like sync.WaitGroup, but for Result values.
 //
-// ResultGroup blocks till last added ably.Result has completed successfully.
+// ResultGroup blocks till last added Result has completed successfully.
 //
-// If at least ably.Result value failed, ResultGroup returns first encountered
+// If at least Result value failed, ResultGroup returns first encountered
 // error immediately.
 type ResultGroup struct {
 	mu    sync.Mutex
@@ -49,7 +53,7 @@ func (rg *ResultGroup) check(err error) bool {
 	return rg.err == nil
 }
 
-func (rg *ResultGroup) Add(res ably.Result, err error) {
+func (rg *ResultGroup) Add(res Result, err error) {
 	if !rg.check(err) {
 		return
 	}
@@ -87,7 +91,7 @@ func (rg *ResultGroup) Wait() error {
 	}
 }
 
-func ConnWaiter(client *ably.Realtime, do func(), expectedEvent ...ably.ConnectionEvent) ably.Result {
+func ConnWaiter(client *ably.Realtime, do func(), expectedEvent ...ably.ConnectionEvent) Result {
 	change := make(chan ably.ConnectionStateChange, 1)
 	off := client.Connection.OnAll(func(ev ably.ConnectionStateChange) {
 		change <- ev
@@ -142,7 +146,7 @@ func (f ResultFunc) Wait(ctx context.Context) error {
 	return f(ctx)
 }
 
-func (f ResultFunc) Go() ably.Result {
+func (f ResultFunc) Go() Result {
 	err := make(chan error, 1)
 	go func() {
 		err <- f(context.Background())
diff --git a/ably/auth_test.go b/ably/auth_test.go
index a4b6c97..9302b43 100644
--- a/ably/auth_test.go
+++ b/ably/auth_test.go
@@ -792,7 +792,7 @@ func TestAuth_CreateTokenRequest(t *testing.T) {
 		if err != nil {
 			ts.Fatalf("CreateTokenRequest()=%v", err)
 		}
-		if req.Mac == "" {
+		if req.MAC == "" {
 			ts.Fatalf("want mac to be not empty")
 		}
 	})
diff --git a/ably/export_test.go b/ably/export_test.go
index e77308a..1811e27 100644
--- a/ably/export_test.go
+++ b/ably/export_test.go
@@ -111,6 +111,12 @@ func WithAfter(after func(context.Context, time.Duration) <-chan time.Time) Clie
 	}
 }
 
+func WithConnectionStateTTL(d time.Duration) ClientOption {
+	return func(os *clientOptions) {
+		os.ConnectionStateTTL = d
+	}
+}
+
 func ApplyOptionsWithDefaults(o ...ClientOption) *clientOptions {
 	return applyOptionsWithDefaults(o...)
 }
@@ -131,3 +137,11 @@ func (c *Connection) PendingItems() int {
 	defer c.mtx.Unlock()
 	return c.pending.Len()
 }
+
+type Result = result
+
+func (c *RESTChannels) Len() int {
+	c.mu.RLock()
+	defer c.mu.RUnlock()
+	return len(c.cache)
+}
diff --git a/ably/realtime_channel_spec_test.go b/ably/realtime_channel_spec_test.go
index a7efa66..97763b4 100644
--- a/ably/realtime_channel_spec_test.go
+++ b/ably/realtime_channel_spec_test.go
@@ -115,7 +115,7 @@ func TestRealtimeChannel_RTL2_ChannelEventForStateChange(t *testing.T) {
 		t.Skip("SUSPENDED not yet implemented")
 	})
 
-	t.Run(fmt.Sprintf("on %s", ably.ChannelEventUpdated), func(t *testing.T) {
+	t.Run(fmt.Sprintf("on %s", ably.ChannelEventUpdate), func(t *testing.T) {
 		t.Parallel()
 
 		t.Skip("UPDATED not yet implemented")
diff --git a/ably/realtime_channel_test.go b/ably/realtime_channel_test.go
index 72d2a6c..879eb1d 100644
--- a/ably/realtime_channel_test.go
+++ b/ably/realtime_channel_test.go
@@ -186,7 +186,7 @@ func TestRealtimeChannel_AttachWhileDisconnected(t *testing.T) {
 	attached := make(ably.ChannelStateChanges, 1)
 	channel.On(ably.ChannelEventAttached, attached.Receive)
 
-	res := make(chan ably.Result)
+	res := make(chan Result)
 	go func() {
 		res <- ablytest.ResultFunc.Go(func(ctx context.Context) error { return channel.Attach(ctx) })
 	}()
diff --git a/ably/realtime_conn_spec_test.go b/ably/realtime_conn_spec_test.go
index e39fffb..c993d60 100644
--- a/ably/realtime_conn_spec_test.go
+++ b/ably/realtime_conn_spec_test.go
@@ -1523,7 +1523,7 @@ func TestRealtimeConn_RTN15h2_Success(t *testing.T) {
 	var change ably.ConnectionStateChange
 	ablytest.Instantly.Recv(t, &change, stateChanges, t.Fatalf)
 
-	if change.Event != ably.ConnectionEventUpdated {
+	if change.Event != ably.ConnectionEventUpdate {
 		t.Fatalf("expected UPDATED event; got %v", change)
 	}
 
diff --git a/ably/rest_client_test.go b/ably/rest_client_test.go
index 8150349..fc187bd 100644
--- a/ably/rest_client_test.go
+++ b/ably/rest_client_test.go
@@ -523,7 +523,7 @@ func TestRESTChannels_RSN1(t *testing.T) {
 	t.Run("RSN4 RSN4a must release channels", func(ts *testing.T) {
 		for _, v := range sample {
 			ch := client.Channels.Get(v.name)
-			client.Channels.Release(ch)
+			client.Channels.Release(ch.Name)
 		}
 		size := client.Channels.Len()
 		if size != 0 {