func TestSubscribe(t *testing.T) {
	h, err := newTestHub()
	assert.NoError(t, err)
	defer h.Close()

	assert.True(t, h.IsRunning(time.Millisecond))

	conn := NewMockConnection()

	/* register the connection and validate registered */
	err = h.Register(conn)
	assert.NoError(t, err)

	topic := &TestTopic{}
	/* Count the number of subscriptions */
	assert.EqualValues(t, 0, h.SubscriptionTopicCount())
	i, err := h.TopicConnectionCount(topic)
	assert.Error(t, err)
	assert.EqualValues(t, 0, i)

	/* add a subscription*/
	err = h.Subscribe(conn, topic, interfacehub.NewFilters(topic))
	assert.NoError(t, err)

	/* Count the number of subscriptions */
	assert.EqualValues(t, 1, h.SubscriptionTopicCount())
	i, err = h.TopicConnectionCount(topic)
	assert.NoError(t, err)
	assert.EqualValues(t, 1, i)

	/* re-add the subscription should fail and counts remain same*/
	err = h.Subscribe(conn, topic, interfacehub.NewFilters(topic))
	assert.Error(t, err)
	assert.EqualValues(t, 1, h.SubscriptionTopicCount())
	i, err = h.TopicConnectionCount(topic)
	assert.NoError(t, err)
	assert.EqualValues(t, 1, i)

	/* subscribe conn nil */
	err = h.Subscribe(nil, topic, interfacehub.NewFilters(topic))
	assert.Error(t, err)

	/* subscribe topic nil */
	err = h.Subscribe(conn, nil, interfacehub.NewFilters(nil))
	assert.Error(t, err)

	/* subscribe filters nil */
	topic2 := &TestTopic2{}
	err = h.Subscribe(conn, topic2, nil)
	assert.NoError(t, err)

	/* subscribe on closed */
	err = h.Close()
	assert.NoError(t, err)
	err = h.Subscribe(conn, topic, interfacehub.NewFilters(topic))
	assert.Error(t, err)
}
func TestNewFilters(t *testing.T) {
	topic := &TestTopic{}
	f := interfacehub.NewFilters(topic)

	assert.IsType(t, &interfacehub.Filters{}, f)
	assert.Equal(t, topic, f.Topic())

}
func TestTopicConnectionCount(t *testing.T) {
	h, err := newTestHub()
	assert.NoError(t, err)
	defer h.Close()

	assert.True(t, h.IsRunning(time.Millisecond))

	conn := NewMockConnection()

	/* register the connection and validate registered */
	err = h.Register(conn)
	assert.NoError(t, err)

	/* Count the number of subscriptions */
	assert.EqualValues(t, 0, h.SubscriptionTopicCount())
	i, err := h.TopicConnectionCount(TestTopic{})
	assert.Error(t, err)
	assert.EqualValues(t, 0, i)

	/* add a subscription*/
	topic := &TestTopic{}
	err = h.Subscribe(conn, topic, interfacehub.NewFilters(topic))
	assert.NoError(t, err)

	/* Re-count the number of subscriptions */
	assert.EqualValues(t, 1, h.SubscriptionTopicCount())
	i, err = h.TopicConnectionCount(topic)
	assert.NoError(t, err)
	assert.EqualValues(t, 1, i)

	/* count nil topic */
	i, err = h.TopicConnectionCount(nil)
	assert.Error(t, err)
	assert.EqualValues(t, 0, i)

}
func TestPublish(t *testing.T) {
	h, err := newTestHub()
	assert.NoError(t, err)
	defer h.Close()

	assert.True(t, h.IsRunning(time.Millisecond))

	conn := NewMockConnection()
	conn2 := NewMockConnection()

	/* register the connection and validate registered */
	err = h.Register(conn)
	assert.NoError(t, err)
	err = h.Register(conn2)
	assert.NoError(t, err)

	/* add a subscription*/
	topic := &TestPublishOutput{}
	err = h.Subscribe(conn, topic, interfacehub.NewFilters(topic))
	assert.NoError(t, err)
	err = h.Subscribe(conn2, topic, interfacehub.NewFilters(topic))
	assert.NoError(t, err)

	pubmsg := make(chan interface{}, 1)
	conn.SetSendDest(pubmsg)

	/* Publish something to and ensure conn don't see it*/
	msg := uniqueString()

	err = h.Publish(conn, &TestPublishOutput{msg: msg})
	assert.NoError(t, err)

	var i interface{}
	select {
	case i = <-pubmsg:
	case <-time.After(time.Millisecond):
	}

	assert.Nil(t, i)

	/* Publish something to and ensure conn does see it*/
	msg = uniqueString()

	err = h.Publish(conn2, &TestPublishOutput{msg: msg})
	assert.NoError(t, err)
	select {
	case i = <-pubmsg:
	case <-time.After(time.Millisecond):
	}

	assert.NotNil(t, i)

	if i != nil {
		p := i.(*TestPublishOutput)
		assert.EqualValues(t, p.msg, msg)
	}

	/* Publish something to delayed connections */
	conn.SetDelay(10 * time.Millisecond)
	err = h.Publish(conn2, &TestPublishOutput{msg: uniqueString()})
	assert.NoError(t, err)
	/* This should take too long and error */
	err = h.Publish(conn2, &TestPublishOutput{msg: uniqueString()})
	assert.Error(t, err)
	conn.SetDelay(time.Duration(0))
	/* conn should be unregistered */
	b, err := h.IsRegistered(conn)
	assert.NoError(t, err)
	assert.False(t, b)

	/* Publish to deregistered connection  */
	err = h.Publish(conn, &TestPublishOutput{msg: uniqueString()})
	assert.Error(t, err)

	/* re-register the connection and validate registered */
	conn = NewMockConnection()
	err = h.Register(conn)
	assert.NoError(t, err)

	/* Publish nil */
	err = h.Publish(conn, nil)
	assert.Error(t, err)

	/* Publish nil conn*/
	err = h.Publish(nil, &TestPublishOutput{msg: "Nothing!"})
	assert.Error(t, err)

	/* Publish to closed */
	err = h.Close()
	assert.NoError(t, err)
	err = h.Publish(conn, &TestPublishOutput{msg: "Hi Testy More!"})
	assert.Error(t, err)
}
func Example() {

	/* create a hub */
	hub, err := interfacehub.NewHub()
	if err != nil {
		log.Fatalf("Unable to create new hub: %s\n", err)
	}
	defer hub.Close()

	/* Done channel to shut stuff down*/
	Done := make(chan bool)

	/* create a client subscribe channel */
	cc := connector.NewIntegratedConnection("interfacehub example client", "Client Conn")
	cc.SetHub(hub)
	err = cc.Register()
	if err != nil {
		log.Fatalf("Unable to regoster client connection: %s\n", err)
	}

	/* Set up the filters for the client to listen on */
	csf := interfacehub.NewFilters(&ExampleServerOutput{})
	csf.Add(&filter.Equal{
		Description:     "Filter to accept specific messages",
		Field:           "Rejected",
		ActionOnMatch:   filter.FILTER_ACTION_DENY,
		ActionOnNoMatch: filter.FILTER_ACTION_CONTINUE,
		ExpectedValue:   true,
	})
	csf.Add(&filter.Equal{
		Description:     "Filter to reject empty messages and accept everything else",
		Field:           "UpperCaseMessage",
		ActionOnMatch:   filter.FILTER_ACTION_DENY,
		ActionOnNoMatch: filter.FILTER_ACTION_ACCEPT,
		ExpectedValue:   "",
	})

	/* set up a client to print the server output received */
	clientmsg := make(chan interface{}, 1)
	cc.SetSendDestination(clientmsg)

	/* have the client print out what it receives */
	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			select {
			case <-Done:
				return
			case i := <-clientmsg:
				v, ok := i.(*ExampleServerOutput)
				if !ok {
					log.Fatal("Expected ExampleServerOutput message")
				}
				fmt.Printf("Echo from server: %s\n", v.UpperCaseMessage)
			}
		}
	}()

	/* do our client subscription to listen for the server response */
	err = cc.Subscribe(&ExampleServerOutput{}, csf)
	if err != nil {
		log.Fatalf("Could not subscribe client: %s\n", err)
	}

	/* create a server subscribe channel */
	sc := connector.NewIntegratedConnection("interfacehub example server", "Server Conn")
	sc.SetHub(hub)
	sc.Register()

	/* set up a server channel to listen  */
	servermsg := make(chan interface{}, 1)
	sc.SetSendDestination(servermsg)

	/* have the server reflect back in upper case */
	wg.Add(1)
	go func(conn *connector.IntegratedConnection) {
		defer wg.Done()
		for {
			select {
			case <-Done:
				return
			case i := <-servermsg:
				v, ok := i.(*ExampleServerInput)
				if !ok {
					log.Fatal("Expected ServerOutput message")
					return
				}
				conn.Publish(&ExampleServerOutput{
					Rejected:         v.Reject,
					UpperCaseMessage: strings.ToUpper(v.Message),
				})
			}
		}
	}(sc)

	/* do our server subscription to listen for the client requests */
	err = sc.Subscribe(&ExampleServerInput{}, nil)
	if err != nil {
		log.Fatalf("Could not subscribe server: %s\n", err)
	}

	/* Send the message*s from the client connection */

	msg := "My client message"
	fmt.Printf("Client Sending to server: %s\n", msg)
	err = cc.Publish(&ExampleServerInput{
		Message: msg,
		Reject:  false,
	})
	if err != nil {
		log.Fatalf("Could not publish to server: %s\n", err)
	}

	time.Sleep(1 * time.Millisecond)

	msg = "Another to be rejected by filter client message"
	fmt.Printf("Client Sending to server: %s\n", msg)
	err = cc.Publish(&ExampleServerInput{
		Message: msg,
		Reject:  true,
	})
	if err != nil {
		log.Fatalf("Could not publish to server: %s\n", err)
	}

	time.Sleep(1 * time.Millisecond)

	msg = "Last client message"
	fmt.Printf("Client Sending to server: %s\n", msg)
	err = cc.Publish(&ExampleServerInput{
		Message: msg,
		Reject:  false,
	})
	if err != nil {
		log.Fatalf("Could not publish to server: %s\n", err)
	}

	time.Sleep(1 * time.Millisecond)

	Done <- true
	Done <- true

	wg.Wait()

	// Output:
	// Client Sending to server: My client message
	// Echo from server: MY CLIENT MESSAGE
	// Client Sending to server: Another to be rejected by filter client message
	// Client Sending to server: Last client message
	// Echo from server: LAST CLIENT MESSAGE
}
func TestClientServerEcho(t *testing.T) {

	/* create a hub */
	hub, _ := interfacehub.NewHub()
	defer hub.Close()

	/* create a client subscribe channel */
	cc := connector.NewIntegratedConnection("interfacehub Unit Tests", "Client Conn")
	cc.SetHub(hub)
	cc.Register()

	/* create a server subscribe channel */
	sc := connector.NewIntegratedConnection("interfacehub Unit Tests", "Server Conn")
	sc.SetHub(hub)
	sc.Register()

	/* set up a client to print the server output received */
	pubmsg := make(chan interface{}, 1)
	cc.SetSendDestination(pubmsg)

	/* Set up the filters for the client to listen on */
	csf := interfacehub.NewFilters(&ServerOutput{})
	csf.Add(&filter.Equal{
		Description:     "Filter to accept specific messages",   // Rule description
		Field:           "UpperCaseMessage",                     // Interface field to match on
		ActionOnMatch:   filter.FILTER_ACTION_ACCEPT,            // Action on match
		ActionOnNoMatch: filter.FILTER_ACTION_CONTINUE,          // Action on not match
		ExpectedValue:   "MESSAGE WE ALWAYS WANT EVEN REJECTED", // Value interface field must match

	})

	csf.Add(&filter.Equal{
		Description:     "Filter to reject tagged messages",
		Field:           "Rejected",
		ActionOnMatch:   filter.FILTER_ACTION_ACCEPT,
		ActionOnNoMatch: filter.FILTER_ACTION_CONTINUE,
		ExpectedValue:   false,
	})
	csf.Add(&filter.Equal{
		Description:     "Everything we want to reject",
		Field:           "UpperCaseMessage",
		ActionOnMatch:   filter.FILTER_ACTION_DENY,
		ActionOnNoMatch: filter.FILTER_ACTION_DENY,
		ExpectedValue:   "",
	})

	/* do our client subscription to listen for the server response*/
	err := cc.Subscribe(&ServerOutput{}, csf)
	assert.NoError(t, err)

	err = sc.Publish(&ServerOutput{
		UpperCaseMessage: "MESSAGE WE ALWAYS WANT EVEN REJECTED",
		Rejected:         true,
	})
	assert.NoError(t, err)

	var i interface{}
	select {
	case i = <-pubmsg:
		v, ok := i.(*ServerOutput)
		assert.True(t, ok)
		assert.Equal(t, "MESSAGE WE ALWAYS WANT EVEN REJECTED", v.UpperCaseMessage)
		assert.True(t, v.Rejected)
	case <-time.After(10 * time.Millisecond):
		assert.Fail(t, "Timeout waiting for ServerOutput message")
	}

	err = sc.Publish(&ServerOutput{
		UpperCaseMessage: "ANOTHER MESSAGE WE WANT IF NOT REJECTED",
		Rejected:         true,
	})
	assert.NoError(t, err)

	select {
	case i = <-pubmsg:
		v, ok := i.(*ServerOutput)
		assert.True(t, ok)
		assert.Equal(t, "Nothing", v.UpperCaseMessage)
	case <-time.After(10 * time.Millisecond):
	}

	err = sc.Publish(&ServerOutput{
		UpperCaseMessage: "ANOTHER MESSAGE WE WANT IF NOT REJECTED",
		Rejected:         false,
	})
	assert.NoError(t, err)

	select {
	case i = <-pubmsg:
		v, ok := i.(*ServerOutput)
		assert.True(t, ok)
		assert.Equal(t, "ANOTHER MESSAGE WE WANT IF NOT REJECTED", v.UpperCaseMessage)
		assert.False(t, v.Rejected)
	case <-time.After(10 * time.Millisecond):
		assert.Fail(t, "Timeout waiting for ServerOutput message")
	}
}
func TestUnsubscribeConnection(t *testing.T) {
	h, err := newTestHub()
	assert.NoError(t, err)
	defer h.Close()

	assert.True(t, h.IsRunning(time.Millisecond))

	conn := NewMockConnection()
	conn2 := NewMockConnection()
	conn3 := NewMockConnection()

	/* register the connection and validate registered */
	err = h.Register(conn)
	assert.NoError(t, err)
	err = h.Register(conn2)
	assert.NoError(t, err)

	/* add a subscription*/
	topic := &TestTopic{}
	err = h.Subscribe(conn, topic, interfacehub.NewFilters(topic))
	assert.NoError(t, err)

	/* add another subscription for same connection different topic*/
	topic3 := &TestTopic2{}
	err = h.Subscribe(conn, topic3, interfacehub.NewFilters(topic3))
	assert.NoError(t, err)

	/* add another subscription for different connection same topic */
	err = h.Subscribe(conn2, topic, interfacehub.NewFilters(topic))
	assert.NoError(t, err)

	/* add another subscription for different connection different topic */
	topic4 := &map[string]string{"key": "value"}
	err = h.Subscribe(conn2, topic4, interfacehub.NewFilters(topic4))
	assert.NoError(t, err)

	/* make sure subscriptions look like we expect */
	assert.EqualValues(t, 3, h.SubscriptionTopicCount())
	i, err := h.TopicConnectionCount(topic)
	assert.NoError(t, err)
	assert.EqualValues(t, 2, i)
	i, err = h.TopicConnectionCount(topic3)
	assert.NoError(t, err)
	assert.EqualValues(t, 1, i)
	i, err = h.TopicConnectionCount(topic4)
	assert.NoError(t, err)
	assert.EqualValues(t, 1, i)

	/* we have 2 subscriptions for conn and 2 subscriptions for conn2 - with overlap in topic */
	/* unsubscribe the whole connection conn */
	err = h.UnsubscribeConnection(conn)
	assert.NoError(t, err)

	/* recount */
	assert.EqualValues(t, 2, h.SubscriptionTopicCount())
	i, err = h.TopicConnectionCount(topic)
	assert.NoError(t, err)
	assert.EqualValues(t, 1, i)
	i, err = h.TopicConnectionCount(topic3)
	assert.Error(t, err)
	assert.EqualValues(t, 0, i)
	i, err = h.TopicConnectionCount(topic4)
	assert.NoError(t, err)
	assert.EqualValues(t, 1, i)

	/* removal of non existent connection */
	err = h.UnsubscribeConnection(conn3)
	assert.Error(t, err)

	/* unsubscribe conn nil */
	err = h.UnsubscribeConnection(nil)
	assert.Error(t, err)
	assert.EqualValues(t, 2, h.SubscriptionTopicCount())

	/* unsubscribe on closed */
	err = h.Close()
	assert.NoError(t, err)
	err = h.UnsubscribeConnection(conn)
	assert.Error(t, err)
	assert.EqualValues(t, 0, h.SubscriptionTopicCount())
}
func TestUnsubscribe(t *testing.T) {
	h, err := newTestHub()
	assert.NoError(t, err)
	defer h.Close()

	assert.True(t, h.IsRunning(time.Millisecond))

	conn := NewMockConnection()
	conn2 := NewMockConnection()

	/* register the connection and validate registered */
	err = h.Register(conn)
	assert.NoError(t, err)
	err = h.Register(conn2)
	assert.NoError(t, err)

	/* Count the number of subscriptions */
	assert.EqualValues(t, 0, h.SubscriptionTopicCount())
	i, err := h.TopicConnectionCount(TestTopic{})
	assert.Error(t, err)
	assert.EqualValues(t, 0, i)

	/* add a subscription*/
	topic := &TestTopic{}
	err = h.Subscribe(conn, topic, interfacehub.NewFilters(topic))
	assert.NoError(t, err)

	/* Re-count the number of subscriptions */
	assert.EqualValues(t, 1, h.SubscriptionTopicCount())
	i, err = h.TopicConnectionCount(topic)
	assert.NoError(t, err)
	assert.EqualValues(t, 1, i)

	/* add another subscription for same connection same topic*/
	topic2 := &TestTopic{}
	err = h.Subscribe(conn, topic2, interfacehub.NewFilters(topic2))
	assert.Error(t, err)
	assert.EqualValues(t, 1, h.SubscriptionTopicCount())
	i, err = h.TopicConnectionCount(topic)
	assert.NoError(t, err)
	assert.EqualValues(t, 1, i)
	i, err = h.TopicConnectionCount(topic2)
	assert.NoError(t, err)
	assert.EqualValues(t, 1, i)

	/* add another subscription for same connection different topic*/
	topic3 := &TestTopic2{}
	err = h.Subscribe(conn, topic3, interfacehub.NewFilters(topic3))
	assert.NoError(t, err)
	assert.EqualValues(t, 2, h.SubscriptionTopicCount())
	i, err = h.TopicConnectionCount(topic)
	assert.NoError(t, err)
	assert.EqualValues(t, 1, i)
	i, err = h.TopicConnectionCount(topic3)
	assert.NoError(t, err)
	assert.EqualValues(t, 1, i)

	/* add another subscription for different connection same topic */
	err = h.Subscribe(conn2, topic, interfacehub.NewFilters(topic))
	assert.NoError(t, err)
	assert.EqualValues(t, 2, h.SubscriptionTopicCount())
	i, err = h.TopicConnectionCount(topic)
	assert.NoError(t, err)
	assert.EqualValues(t, 2, i)

	/* add another subscription for different connection different topic */
	topic4 := &map[string]string{"key": "value"}
	err = h.Subscribe(conn2, topic4, interfacehub.NewFilters(topic4))
	assert.NoError(t, err)
	assert.EqualValues(t, 3, h.SubscriptionTopicCount())
	i, err = h.TopicConnectionCount(topic)
	assert.NoError(t, err)
	assert.EqualValues(t, 2, i)

	/* add another subscription with mismatched subscription topic and filters topic */
	/* add another subscription for different connection different topic */
	err = h.Subscribe(conn2, topic4, interfacehub.NewFilters(topic))
	assert.Error(t, err)
	assert.EqualValues(t, 3, h.SubscriptionTopicCount())
	i, err = h.TopicConnectionCount(topic)
	assert.NoError(t, err)
	assert.EqualValues(t, 2, i)

	/* remove the topic subscription*/
	err = h.Unsubscribe(conn, topic)
	assert.NoError(t, err)
	assert.EqualValues(t, 3, h.SubscriptionTopicCount())
	i, err = h.TopicConnectionCount(topic)
	assert.NoError(t, err)
	assert.EqualValues(t, 1, i)

	err = h.Unsubscribe(conn2, topic)
	assert.NoError(t, err)
	assert.EqualValues(t, 2, h.SubscriptionTopicCount())
	i, err = h.TopicConnectionCount(topic)
	assert.Error(t, err)
	assert.EqualValues(t, 0, i)

	/* removal of non existent subscription */
	err = h.Unsubscribe(conn2, topic)
	assert.Error(t, err)
	assert.EqualValues(t, 2, h.SubscriptionTopicCount())
	i, err = h.TopicConnectionCount(topic)
	assert.Error(t, err)
	assert.EqualValues(t, 0, i)

	/* remove the topic3 subscription*/
	err = h.Unsubscribe(conn, topic3)
	assert.NoError(t, err)
	assert.EqualValues(t, 1, h.SubscriptionTopicCount())
	i, err = h.TopicConnectionCount(topic3)
	assert.Error(t, err)
	assert.EqualValues(t, 0, i)

	/* remove the topic4 subscription*/
	err = h.Unsubscribe(conn2, topic4)
	assert.NoError(t, err)
	assert.EqualValues(t, 0, h.SubscriptionTopicCount())
	i, err = h.TopicConnectionCount(topic4)
	assert.Error(t, err)
	assert.EqualValues(t, 0, i)

	/* unsubscribe conn nil */
	err = h.Unsubscribe(nil, topic4)
	assert.Error(t, err)
	assert.EqualValues(t, 0, h.SubscriptionTopicCount())

	/* unsubscribe conn nil */
	err = h.Unsubscribe(conn, nil)
	assert.Error(t, err)
	assert.EqualValues(t, 0, h.SubscriptionTopicCount())

	/* unsubscribe on closed */
	err = h.Close()
	assert.NoError(t, err)
	err = h.Unsubscribe(conn, topic4)
	assert.Error(t, err)
	assert.EqualValues(t, 0, h.SubscriptionTopicCount())
}