blob: d4b7b271a2f117ea94e63146c45996da9169febf [file] [log] [blame]
// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package util_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/apid/apid-core/util"
"math/rand"
"net/http"
"net/http/httptest"
"regexp"
"sync/atomic"
"testing"
"time"
)
var _ = BeforeSuite(func() {
})
func TestEvents(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Util Suite")
}
var _ = Describe("APID utils", func() {
Context("UUID", func() {
It("should validate UUID", func() {
data := []string{
"fc041ed2-62a7-4086-a66e-bbbc9219fad5",
"invalid-uuid",
}
expected := []bool{
true,
false,
}
for i := range data {
Ω(util.IsValidUUID(data[i])).Should(Equal(expected[i]))
}
})
It("should generate valid UUID", func() {
r := regexp.MustCompile("^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$")
Ω(r.MatchString(util.GenerateUUID())).Should(BeTrue())
Ω(util.IsValidUUID(util.GenerateUUID())).Should(BeTrue())
})
})
Context("Forward Proxy Protocol", func() {
It("Verify Forward proxying to server works", func() {
var maxIdleConnsPerHost = 10
var tr *http.Transport
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Fail("Cant come here, as we have not forwarded request from fwdPrxyServer")
}))
fwdPrxyServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expect(r.Header.Get("foo")).Should(Equal("bar"))
w.Header().Set("bar", "foo")
}))
tr = util.Transport(fwdPrxyServer.URL)
tr.MaxIdleConnsPerHost = maxIdleConnsPerHost
var rspcnt int = 0
ch := make(chan *http.Response)
client := &http.Client{Transport: tr}
for i := 0; i < 2*maxIdleConnsPerHost; i++ {
go func(client *http.Client) {
defer GinkgoRecover()
req, err := http.NewRequest("GET", server.URL, nil)
Expect(err).Should(Succeed())
req.Header.Set("foo", "bar")
resp, err := client.Do(req)
Expect(err).Should(Succeed())
Expect(resp.Header.Get("bar")).Should(Equal("foo"))
resp.Body.Close()
ch <- resp
}(client)
}
for {
resp := <-ch
Expect(resp.StatusCode).To(Equal(http.StatusOK))
if rspcnt >= 2*maxIdleConnsPerHost-1 {
return
}
rspcnt++
}
}, 3)
})
Context("Long polling utils", func() {
It("DistributeEvents", func() {
// make test data
deliverChan := make(chan interface{})
addSubscriber := make(chan chan interface{})
subs := make([]chan interface{}, 50+rand.Intn(50))
for i := range subs {
subs[i] = make(chan interface{}, 1)
}
// test
go util.DistributeEvents(deliverChan, addSubscriber)
for i := range subs {
go func(j int) {
addSubscriber <- subs[j]
}(i)
}
n := rand.Int()
closed := new(int32)
go func(c *int32) {
for atomic.LoadInt32(c) == 0 {
deliverChan <- n
}
}(closed)
for i := range subs {
Ω(<-subs[i]).Should(Equal(n))
}
atomic.StoreInt32(closed, 1)
}, 2)
It("Long polling", func(done Done) {
// make test data
deliverChan := make(chan interface{})
addSubscriber := make(chan chan interface{})
go util.DistributeEvents(deliverChan, addSubscriber)
n := rand.Int()
closed := new(int32)
successHandler := func(e interface{}, w http.ResponseWriter) {
defer GinkgoRecover()
Ω(w).Should(BeNil())
Ω(e).Should(Equal(n))
atomic.StoreInt32(closed, 1)
close(done)
}
timeoutHandler := func(w http.ResponseWriter) {}
// Long polling
go util.LongPolling(nil, time.Minute, addSubscriber, successHandler, timeoutHandler)
go func(c *int32) {
for atomic.LoadInt32(c) == 0 {
deliverChan <- n
}
}(closed)
})
It("Long polling timeout", func(done Done) {
// make test data
deliverChan := make(chan interface{})
addSubscriber := make(chan chan interface{})
go util.DistributeEvents(deliverChan, addSubscriber)
successHandler := func(e interface{}, w http.ResponseWriter) {}
timeoutHandler := func(w http.ResponseWriter) {
defer GinkgoRecover()
Ω(w).Should(BeNil())
close(done)
}
// Long polling
go util.LongPolling(nil, time.Second, addSubscriber, successHandler, timeoutHandler)
}, 2)
It("Debounce", func() {
// make test data
data := make(map[int]int)
inChan := make(chan interface{})
outChan := make(chan []interface{})
go util.Debounce(inChan, outChan, time.Second)
for i := 0; i < 5+rand.Intn(5); i++ {
n := rand.Int()
data[n]++
go func(j int) {
inChan <- j
}(n)
}
// Debounce
e := <-outChan
for _, m := range e {
num, ok := m.(int)
Ω(ok).Should(BeTrue())
data[num]--
}
for _, v := range data {
Ω(v).Should(BeZero())
}
}, 2)
})
It("Contains", func() {
Expect(util.Contains([]string{"foo", "bar"}, "foo")).Should(BeTrue())
Expect(util.Contains([]string{"foo", "bar"}, "bar")).Should(BeTrue())
Expect(util.Contains([]string{"foo", "bar"}, "xxx")).Should(BeFalse())
Expect(util.Contains([]string{}, "foo")).Should(BeFalse())
})
})