mirror of https://github.com/portainer/portainer
218 lines
5.4 KiB
Go
218 lines
5.4 KiB
Go
package offlinegate
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func Test_canLockAndUnlock(t *testing.T) {
|
|
o := NewOfflineGate()
|
|
|
|
unlock := o.Lock()
|
|
unlock()
|
|
}
|
|
|
|
func Test_hasToBeUnlockedToLockAgain(t *testing.T) {
|
|
// scenario:
|
|
// 1. first routine starts and locks the gate
|
|
// 2. first routine starts a second and wait for the second to start
|
|
// 3. second start but waits for the gate to be released
|
|
// 4. first continues and unlocks the gate, when done
|
|
// 5. second be able to continue
|
|
// 6. second lock the gate, does the job and unlocks it
|
|
|
|
o := NewOfflineGate()
|
|
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(2)
|
|
|
|
result := make([]string, 0, 2)
|
|
go func() {
|
|
unlock := o.Lock()
|
|
defer unlock()
|
|
waitForSecondToStart := sync.WaitGroup{}
|
|
waitForSecondToStart.Add(1)
|
|
go func() {
|
|
waitForSecondToStart.Done()
|
|
unlock := o.Lock()
|
|
defer unlock()
|
|
result = append(result, "second")
|
|
wg.Done()
|
|
}()
|
|
waitForSecondToStart.Wait()
|
|
result = append(result, "first")
|
|
wg.Done()
|
|
}()
|
|
|
|
wg.Wait()
|
|
|
|
if len(result) != 2 || result[0] != "first" || result[1] != "second" {
|
|
t.Error("Second call have disresregarded a raised lock")
|
|
}
|
|
|
|
}
|
|
|
|
func Test_waitChannelWillBeEmpty_ifGateIsUnlocked(t *testing.T) {
|
|
o := NewOfflineGate()
|
|
|
|
signalingCh := o.Watch()
|
|
if signalingCh != nil {
|
|
t.Error("Signaling channel should be empty")
|
|
}
|
|
}
|
|
|
|
func Test_startWaitingForSignal_beforeGateGetsUnlocked(t *testing.T) {
|
|
// scenario:
|
|
// 1. main routing locks the gate and waits for a consumer to start up
|
|
// 2. consumer starts up, notifies main and begins waiting for the gate to be unlocked
|
|
// 3. main unlocks the gate
|
|
// 4. consumer be able to continue
|
|
|
|
o := NewOfflineGate()
|
|
unlock := o.Lock()
|
|
|
|
signalingCh := o.Watch()
|
|
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(1)
|
|
readerIsReady := sync.WaitGroup{}
|
|
readerIsReady.Add(1)
|
|
|
|
go func(t *testing.T) {
|
|
readerIsReady.Done()
|
|
|
|
// either wait for a signal or timeout
|
|
select {
|
|
case <-signalingCh:
|
|
case <-time.After(10 * time.Second):
|
|
t.Error("Failed to wait for a signal, exit by timeout")
|
|
}
|
|
wg.Done()
|
|
}(t)
|
|
|
|
readerIsReady.Wait()
|
|
unlock()
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
func Test_startWaitingForSignal_afterGateGetsUnlocked(t *testing.T) {
|
|
// scenario:
|
|
// 1. main routing locks, gets waiting channel and unlocks
|
|
// 2. consumer starts up and begins waiting for the gate to be unlocked
|
|
// 3. consumer gets signal immediately and continues
|
|
|
|
o := NewOfflineGate()
|
|
unlock := o.Lock()
|
|
signalingCh := o.Watch()
|
|
unlock()
|
|
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(1)
|
|
|
|
go func(t *testing.T) {
|
|
// either wait for a signal or timeout
|
|
select {
|
|
case <-signalingCh:
|
|
case <-time.After(10 * time.Second):
|
|
t.Error("Failed to wait for a signal, exit by timeout")
|
|
}
|
|
wg.Done()
|
|
}(t)
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
func Test_waitingMiddleware_executesImmediately_whenNotLocked(t *testing.T) {
|
|
// scenario:
|
|
// 1. create an gate
|
|
// 2. kick off a waiting middleware that will release immediately as gate wasn't locked
|
|
// 3. middleware shouldn't timeout
|
|
|
|
o := NewOfflineGate()
|
|
|
|
request := httptest.NewRequest(http.MethodPost, "/", nil)
|
|
response := httptest.NewRecorder()
|
|
|
|
timeout := 2 * time.Second
|
|
start := time.Now()
|
|
o.WaitingMiddleware(timeout, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
elapsed := time.Since(start)
|
|
if elapsed >= timeout {
|
|
t.Error("WaitingMiddleware had likely timeout, when it shouldn't")
|
|
}
|
|
w.Write([]byte("success"))
|
|
})).ServeHTTP(response, request)
|
|
|
|
body, _ := io.ReadAll(response.Body)
|
|
if string(body) != "success" {
|
|
t.Error("Didn't receive expected result from the hanlder")
|
|
}
|
|
}
|
|
|
|
func Test_waitingMiddleware_waitsForTheLockToBeReleased(t *testing.T) {
|
|
// scenario:
|
|
// 1. create an gate and lock it
|
|
// 2. kick off a routing that will unlock the gate after 1 second
|
|
// 3. kick off a waiting middleware that will wait for lock to be eventually released
|
|
// 4. middleware shouldn't timeout
|
|
|
|
o := NewOfflineGate()
|
|
unlock := o.Lock()
|
|
|
|
request := httptest.NewRequest(http.MethodPost, "/", nil)
|
|
response := httptest.NewRecorder()
|
|
|
|
go func() {
|
|
time.Sleep(1 * time.Second)
|
|
unlock()
|
|
}()
|
|
|
|
timeout := 10 * time.Second
|
|
start := time.Now()
|
|
o.WaitingMiddleware(timeout, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
elapsed := time.Since(start)
|
|
if elapsed >= timeout {
|
|
t.Error("WaitingMiddleware had likely timeout, when it shouldn't")
|
|
}
|
|
w.Write([]byte("success"))
|
|
})).ServeHTTP(response, request)
|
|
|
|
body, _ := io.ReadAll(response.Body)
|
|
if string(body) != "success" {
|
|
t.Error("Didn't receive expected result from the hanlder")
|
|
}
|
|
}
|
|
|
|
func Test_waitingMiddleware_mayTimeout_whenLockedForTooLong(t *testing.T) {
|
|
/*
|
|
scenario:
|
|
1. create an gate and lock it
|
|
2. kick off a waiting middleware that will wait for lock to be eventually released
|
|
3. because we never unlocked the gate, middleware suppose to timeout
|
|
*/
|
|
o := NewOfflineGate()
|
|
o.Lock()
|
|
|
|
request := httptest.NewRequest(http.MethodPost, "/", nil)
|
|
response := httptest.NewRecorder()
|
|
|
|
timeout := 1 * time.Second
|
|
start := time.Now()
|
|
o.WaitingMiddleware(timeout, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
elapsed := time.Since(start)
|
|
if elapsed < timeout {
|
|
t.Error("WaitingMiddleware suppose to timeout, but it didnt")
|
|
}
|
|
w.Write([]byte("success"))
|
|
})).ServeHTTP(response, request)
|
|
|
|
assert.Equal(t, http.StatusRequestTimeout, response.Result().StatusCode, "Request support to timeout waiting for the gate")
|
|
}
|