text: package aat
  
  import (
  	"context"
  	"errors"
  	"fmt"
  	"testing"
  	"time"
  
  	"github.com/gammazero/nexus/v3/client"
  	"github.com/gammazero/nexus/v3/wamp"
  )
  
  const (
  	metaOnCreate      = string(wamp.MetaEventSubOnCreate)
  	metaOnSubscribe   = string(wamp.MetaEventSubOnSubscribe)
  	metaOnUnsubscribe = string(wamp.MetaEventSubOnUnsubscribe)
  	metaOnDelete      = string(wamp.MetaEventSubOnDelete)
  
  	metaSubGet = string(wamp.MetaProcSubGet)
  )
  
  func TestMetaEventOnCreateOnSubscribe(t *testing.T) {
  	// Connect subscriber session.
  	subscriber, err := connectClient()
  	if err != nil {
  		t.Fatal("Failed to connect client:", err)
  	}
  
  	// Check for feature support in router.
  	const featureSubMetaAPI = "subscription_meta_api"
  	if !subscriber.HasFeature("broker", featureSubMetaAPI) {
  		t.Error("Broker does not have", featureSubMetaAPI, "feature")
  	}
  
  	// Subscribe to on_create meta event.
  	onCreateEvents := make(chan *wamp.Event)
  	err = subscriber.SubscribeChan(metaOnCreate, onCreateEvents, nil)
  	if err != nil {
  		t.Fatal("subscribe error:", err)
  	}
  
  	// Subscribe to on_subscribe meta event.
  	onSubEvents := make(chan *wamp.Event)
  	err = subscriber.SubscribeChan(metaOnSubscribe, onSubEvents, nil)
  	if err != nil {
  		t.Fatal("subscribe error:", err)
  	}
  
  	select {
  	case <-onCreateEvents:
  		t.Fatal("Received on_create when subscribing to meta event")
  	case <-onSubEvents:
  		t.Fatal("Received on_subscribe when subscribing to meta event")
  	case <-time.After(200 * time.Millisecond):
  	}
  
  	// Connect another client.
  	sess, err := connectClient()
  	if err != nil {
  		t.Fatal("Failed to connect client:", err)
  	}
  
  	// Subscribe to something to generate meta on_subcribe event
  	nullHandler := func(_ *wamp.Event) { return }
  	err = sess.Subscribe("some.topic", nullHandler, nil)
  	if err != nil {
  		t.Fatal("subscribe error:", err)
  	}
  	subID, ok := sess.SubscriptionID("some.topic")
  	if !ok {
  		t.Fatal("client does not have subscription ID")
  	}
  
  	var onCreateID, onCreateSessID wamp.ID
  	onCreate := func(event *wamp.Event) error {
  		args := event.Arguments
  		if len(args) != 2 {
  			return errors.New("wrong number of arguments")
  		}
  		dict, ok := wamp.AsDict(args[1])
  		if !ok {
  			return errors.New("arg 1 was not wamp.Dict")
  		}
  		onCreateSessID, ok = wamp.AsID(args[0])
  		if !ok {
  			return errors.New("argument 0 (session) was not wamp.ID")
  		}
  		onCreateID, _ = wamp.AsID(dict["id"])
  		if u, _ := wamp.AsURI(dict["uri"]); u != wamp.URI("some.topic") {
  			return fmt.Errorf(
  				"on_create had wrong topic, got '%v' want 'some.topic'", u)
  		}
  		if s, _ := wamp.AsString(dict["created"]); s == "" {
  			return errors.New("on_create missing created time")
  		}
  		return nil
  	}
  
  	// Make sure the on_create event was received.
  	var event *wamp.Event
  	select {
  	case event = <-onCreateEvents:
  		if err = onCreate(event); err != nil {
  			t.Fatal("Event error:", err)
  		}
  	case <-time.After(200 * time.Millisecond):
  		t.Fatal("did not get", metaOnCreate, "event")
  	}
  	if onCreateSessID != sess.ID() {
  		t.Fatal(metaOnCreate, "meta even had wrong session ID")
  	}
  	if onCreateID != subID {
  		t.Fatal("meta event did not return expected subscription ID")
  	}
  
  	var onSubSubID, onSubSessID wamp.ID
  	onSub := func(event *wamp.Event) error {
  		args := event.Arguments
  		if len(args) != 2 {
  			return errors.New("wrong number of arguments")
  		}
  		var ok bool
  		onSubSessID, ok = wamp.AsID(args[0])
  		if !ok {
  			return errors.New("argument 0 (session) was not wamp.ID")
  		}
  		onSubSubID, ok = wamp.AsID(args[1])
  		if !ok {
  			return errors.New("argument 1 (subscription) was not wamp.ID")
  		}
  		return nil
  	}
  
  	// Make sure the on_subscribe event was received.
  	select {
  	case event = <-onSubEvents:
  		if err = onSub(event); err != nil {
  			t.Fatal("Event error:", err)
  		}
  	case <-time.After(200 * time.Millisecond):
  		t.Fatal("did not get", metaOnSubscribe, "event")
  	}
  	if onSubSessID != sess.ID() {
  		t.Fatal(metaOnSubscribe, "meta even had wrong session ID")
  	}
  	if onSubSubID != subID {
  		t.Fatal("meta event did not return expected subscription ID")
  	}
  
  	err = sess.Unsubscribe("some.topic")
  	if err != nil {
  		t.Fatal("unsubscribe error:", err)
  	}
  
  	err = subscriber.Unsubscribe(metaOnSubscribe)
  	if err != nil {
  		t.Fatal("unsubscribe error:", err)
  	}
  
  	err = sess.Close()
  	if err != nil {
  		t.Fatal("Failed to disconnect client:", err)
  	}
  
  	err = subscriber.Close()
  	if err != nil {
  		t.Fatal("Failed to disconnect client:", err)
  	}
  }
  
  func TestMetaEventOnUnsubscribeOnDelete(t *testing.T) {
  	// Connect subscriber session.
  	subscriber, err := connectClient()
  	if err != nil {
  		t.Fatal("Failed to connect client:", err)
  	}
  
  	// Subscribe to on_unsubscribe event.
  	onUnsubEvents := make(chan *wamp.Event)
  	err = subscriber.SubscribeChan(metaOnUnsubscribe, onUnsubEvents, nil)
  	if err != nil {
  		t.Fatal("subscribe error:", err)
  	}
  
  	// Subscribe to on_delete event.
  	onDelEvents := make(chan *wamp.Event)
  	err = subscriber.SubscribeChan(metaOnDelete, onDelEvents, nil)
  	if err != nil {
  		t.Fatal("subscribe error:", err)
  	}
  
  	select {
  	case <-onUnsubEvents:
  		t.Fatal("Received on_unsubscribe when unsubscribing to meta event")
  	case <-onDelEvents:
  		t.Fatal("Received on_delete when unsubscribing to meta event")
  	case <-time.After(200 * time.Millisecond):
  	}
  
  	// Connect another client.
  	sess, err := connectClient()
  	if err != nil {
  		t.Fatal("Failed to connect client:", err)
  	}
  
  	// Subscribe to something and then unsubscribe to generate meta on_subcribe
  	// event.
  	nullHandler := func(_ *wamp.Event) { return }
  	if err = sess.Subscribe("some.topic", nullHandler, nil); err != nil {
  		t.Fatal("subscribe error:", err)
  	}
  	subID, ok := sess.SubscriptionID("some.topic")
  	if !ok {
  		t.Fatal("client does not have subscription ID")
  	}
  	if err = sess.Unsubscribe("some.topic"); err != nil {
  		t.Fatal("unsubscribe error:", err)
  	}
  
  	var onUnsubSubID, onUnsubSessID wamp.ID
  	onUnsub := func(event *wamp.Event) error {
  		args := event.Arguments
  		if len(args) != 2 {
  			return errors.New("wrong number of arguments")
  		}
  		var ok bool
  		onUnsubSessID, ok = wamp.AsID(args[0])
  		if !ok {
  			return errors.New("argument 0 (session) was not wamp.ID")
  		}
  		onUnsubSubID, ok = wamp.AsID(args[1])
  		if !ok {
  			return errors.New("argument 1 (subscription) was not wamp.ID")
  		}
  		return nil
  	}
  
  	// Make sure the unsubscribe event was received.
  	var event *wamp.Event
  	select {
  	case event := <-onUnsubEvents:
  		if err = onUnsub(event); err != nil {
  			t.Fatal("Event error:", err)
  		}
  	case <-time.After(200 * time.Millisecond):
  		t.Fatal("did not get", metaOnUnsubscribe, "event")
  	}
  	if onUnsubSessID != sess.ID() {
  		t.Fatal(metaOnUnsubscribe, "meta even had wrong session ID")
  	}
  	if onUnsubSubID != subID {
  		t.Fatal("meta event did not return expected subscription ID")
  	}
  
  	var onDelSubID, onDelSessID wamp.ID
  	onDel := func(event *wamp.Event) error {
  		args := event.Arguments
  		if len(args) != 2 {
  			return errors.New("wrong number of arguments")
  		}
  		var ok bool
  		onDelSessID, ok = wamp.AsID(args[0])
  		if !ok {
  			return errors.New("argument 0 (session) was not wamp.ID")
  		}
  		onDelSubID, ok = wamp.AsID(args[1])
  		if !ok {
  			return errors.New("argument 1 (subscription) was not wamp.ID")
  		}
  		return nil
  	}
  
  	// Make sure the delete event was received.
  	select {
  	case event = <-onDelEvents:
  		if err = onDel(event); err != nil {
  			t.Fatal("Event error:", err)
  		}
  	case <-time.After(200 * time.Millisecond):
  		t.Fatal("did not get", metaOnDelete, "event")
  	}
  	if onDelSessID != sess.ID() {
  		t.Fatal(metaOnUnsubscribe, "meta even had wrong session ID")
  	}
  	if onDelSubID != subID {
  		t.Fatal("meta event did not return expected subscription ID")
  	}
  
  	err = subscriber.Unsubscribe(metaOnUnsubscribe)
  	if err != nil {
  		t.Fatal("unsubscribe error:", err)
  	}
  
  	err = sess.Close()
  	if err != nil {
  		t.Fatal("Failed to disconnect client:", err)
  	}
  
  	err = subscriber.Close()
  	if err != nil {
  		t.Fatal("Failed to disconnect client:", err)
  	}
  }
  
  func TestMetaProcSubGet(t *testing.T) {
  	// Connect subscriber session.
  	subscriber, err := connectClient()
  	if err != nil {
  		t.Fatal("Failed to connect client:", err)
  	}
  	defer subscriber.Close()
  
  	// Subscribe to something
  	nullHandler := func(_ *wamp.Event) { return }
  	err = subscriber.Subscribe(testTopicWC, nullHandler, wamp.Dict{"match": "wildcard"})
  	if err != nil {
  		t.Fatal("subscribe error:", err)
  	}
  	subID, ok := subscriber.SubscriptionID(testTopicWC)
  	if !ok {
  		t.Fatal("client does not have subscription ID")
  	}
  
  	// Connect caller
  	caller, err := connectClient()
  	if err != nil {
  		t.Fatal("Failed to connect client:", err)
  	}
  	defer caller.Close()
  
  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
  	defer cancel()
  	result, err := caller.Call(ctx, metaSubGet, nil, wamp.List{subID}, nil, nil)
  	if err != nil {
  		t.Fatal("error calling", metaSubGet, err)
  	}
  	subDetails, ok := wamp.AsDict(result.Arguments[0])
  	if !ok {
  		t.Fatal("Result argument is not dict")
  	}
  	id, _ := wamp.AsID(subDetails["id"])
  	if id != subID {
  		t.Error("Received wrong subscription ID")
  	}
  	m, _ := wamp.AsString(subDetails["match"])
  	if m != "wildcard" {
  		t.Error("subscription does not have wildcard policy, has", m)
  	}
  	uri, _ := wamp.AsURI(subDetails["uri"])
  	if uri != testTopicWC {
  		t.Error("subscription has wrong topic URI")
  	}
  
  	err = subscriber.Unsubscribe(testTopicWC)
  	if err != nil {
  		t.Fatal("unsubscribe error:", err)
  	}
  
  	cancel()
  	ctx, cancel = context.WithTimeout(context.Background(), time.Second)
  	defer cancel()
  	result, err = caller.Call(ctx, metaSubGet, nil, wamp.List{subID}, nil, nil)
  	if err == nil {
  		t.Fatal("expected error")
  	}
  	rpcErr, ok := err.(client.RPCError)
  	if !ok {
  		t.Fatal("expected error to be client.RPCError")
  	}
  	if rpcErr.Err.Error != wamp.ErrNoSuchSubscription {
  		t.Fatal("wrong error:", rpcErr.Err.Error)
  	}
  }
  