shared-sqs: Этап 1 — клон GoAWS, удаление SNS, go build OK
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func ChangeMessageVisibilityV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
|
||||
requestBody := models.NewChangeMessageVisibilityRequest()
|
||||
ok := utils.REQUEST_TRANSFORMER(requestBody, req, false)
|
||||
if !ok {
|
||||
log.Error("Invalid Request - ChangeMessageVisibilityV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
|
||||
vars := mux.Vars(req)
|
||||
|
||||
queueUrl := requestBody.QueueUrl
|
||||
queueName := ""
|
||||
if queueUrl == "" {
|
||||
queueName = vars["queueName"]
|
||||
} else {
|
||||
uriSegments := strings.Split(queueUrl, "/")
|
||||
queueName = uriSegments[len(uriSegments)-1]
|
||||
}
|
||||
|
||||
receiptHandle := requestBody.ReceiptHandle
|
||||
|
||||
visibilityTimeout := requestBody.VisibilityTimeout
|
||||
if visibilityTimeout > 43200 {
|
||||
return utils.CreateErrorResponseV1("ValidationError", true)
|
||||
}
|
||||
|
||||
if _, ok := models.SyncQueues.Queues[queueName]; !ok {
|
||||
return utils.CreateErrorResponseV1("QueueNotFound", true)
|
||||
}
|
||||
|
||||
models.SyncQueues.Lock()
|
||||
messageFound := false
|
||||
for i := 0; i < len(models.SyncQueues.Queues[queueName].Messages); i++ {
|
||||
queue := models.SyncQueues.Queues[queueName]
|
||||
msgs := queue.Messages
|
||||
if msgs[i].ReceiptHandle == receiptHandle {
|
||||
timeout := models.SyncQueues.Queues[queueName].VisibilityTimeout
|
||||
if visibilityTimeout == 0 {
|
||||
msgs[i].ReceiptTime = time.Now().UTC()
|
||||
msgs[i].ReceiptHandle = ""
|
||||
msgs[i].VisibilityTimeout = time.Now().Add(time.Duration(timeout) * time.Second)
|
||||
msgs[i].Retry++
|
||||
if queue.MaxReceiveCount > 0 &&
|
||||
queue.DeadLetterQueue != nil &&
|
||||
msgs[i].Retry >= queue.MaxReceiveCount {
|
||||
queue.DeadLetterQueue.Messages = append(queue.DeadLetterQueue.Messages, msgs[i])
|
||||
queue.Messages = append(queue.Messages[:i], queue.Messages[i+1:]...)
|
||||
}
|
||||
} else {
|
||||
msgs[i].VisibilityTimeout = time.Now().Add(time.Duration(visibilityTimeout) * time.Second)
|
||||
}
|
||||
messageFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
models.SyncQueues.Unlock()
|
||||
if !messageFound {
|
||||
return utils.CreateErrorResponseV1("MessageNotInFlight", true)
|
||||
}
|
||||
|
||||
respStruct := models.ChangeMessageVisibilityResult{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
|
||||
return http.StatusOK, &respStruct
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"shared-sqs/app/test"
|
||||
|
||||
"shared-sqs/app/fixtures"
|
||||
"shared-sqs/app/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestChangeMessageVisibility_success(t *testing.T) {
|
||||
// create a queue
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
}()
|
||||
|
||||
q := &models.Queue{
|
||||
Name: "testing",
|
||||
Messages: []models.SqsMessage{{
|
||||
MessageBody: "test1",
|
||||
ReceiptHandle: "123",
|
||||
}},
|
||||
}
|
||||
models.SyncQueues.Queues["testing"] = q
|
||||
|
||||
// The default value for the VisibilityTimeout is the zero value of time.Time
|
||||
assert.Zero(t, q.Messages[0].VisibilityTimeout)
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", models.ChangeMessageVisibilityRequest{
|
||||
QueueUrl: "http://localhost:4100/queue/testing",
|
||||
ReceiptHandle: "123",
|
||||
VisibilityTimeout: 0,
|
||||
}, true)
|
||||
status, _ := ChangeMessageVisibilityV1(r)
|
||||
assert.Equal(t, status, http.StatusOK)
|
||||
|
||||
// Changing the message visibility increments the time.Time by N seconds
|
||||
// from the current time.
|
||||
//
|
||||
// Given that the current time is relative between calling the endpoint and
|
||||
// the time being set, we can't reliably assert an exact value. So assert
|
||||
// that the time.Time value is no longer the default zero value.
|
||||
assert.NotZero(t, q.Messages[0].VisibilityTimeout)
|
||||
assert.NotZero(t, q.Messages[0].ReceiptTime)
|
||||
assert.Equal(t, "", q.Messages[0].ReceiptHandle)
|
||||
assert.Equal(t, 1, q.Messages[0].Retry)
|
||||
}
|
||||
|
||||
func TestChangeMessageVisibility_success_adds_to_existing_visibility_timeout(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func TestChangeMessageVisibility_success_transfers_to_dead_letter_queue(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func TestChangeMessageVisibility_request_transformer_error(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func TestChangeMessageVisibility_visibility_timeout_too_large(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func TestChangeMessageVisibility_missing_queue(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func TestChangeMessageVisibility_missing_message(t *testing.T) {
|
||||
// TODO - mismatch receipt handle
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func CreateQueueV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
|
||||
requestBody := models.NewCreateQueueRequest()
|
||||
ok := utils.REQUEST_TRANSFORMER(requestBody, req, false)
|
||||
if !ok {
|
||||
log.Error("Invalid Request - CreateQueueV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
queueName := requestBody.QueueName
|
||||
|
||||
queueUrl := "http://" + models.CurrentEnvironment.Host + ":" + models.CurrentEnvironment.Port +
|
||||
"/" + models.CurrentEnvironment.AccountID + "/" + queueName
|
||||
if models.CurrentEnvironment.Region != "" {
|
||||
queueUrl = "http://" + models.CurrentEnvironment.Region + "." + models.CurrentEnvironment.Host + ":" +
|
||||
models.CurrentEnvironment.Port + "/" + models.CurrentEnvironment.AccountID + "/" + queueName
|
||||
}
|
||||
queueArn := "arn:aws:sqs:" + models.CurrentEnvironment.Region + ":" + models.CurrentEnvironment.AccountID + ":" + queueName
|
||||
|
||||
if _, ok := models.SyncQueues.Queues[queueName]; !ok {
|
||||
log.Infof("Creating Queue: %s", queueName)
|
||||
queue := &models.Queue{
|
||||
Name: queueName,
|
||||
URL: queueUrl,
|
||||
Arn: queueArn,
|
||||
IsFIFO: utils.HasFIFOQueueName(queueName),
|
||||
EnableDuplicates: models.CurrentEnvironment.EnableDuplicates,
|
||||
Duplicates: make(map[string]time.Time),
|
||||
}
|
||||
if err := setQueueAttributesV1(queue, requestBody.Attributes); err != nil {
|
||||
return utils.CreateErrorResponseV1(err.Error(), true)
|
||||
}
|
||||
models.SyncQueues.Lock()
|
||||
models.SyncQueues.Queues[queueName] = queue
|
||||
models.SyncQueues.Unlock()
|
||||
}
|
||||
|
||||
respStruct := models.CreateQueueResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Result: models.CreateQueueResult{QueueUrl: queueUrl},
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
return http.StatusOK, respStruct
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"shared-sqs/app/test"
|
||||
|
||||
"shared-sqs/app/fixtures"
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/mitchellh/copystructure"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCreateQueueV1_success(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.CreateQueueRequest)
|
||||
*v = fixtures.CreateQueueRequest
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := CreateQueueV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, fixtures.CreateQueueResponse, response)
|
||||
|
||||
actualQueue := models.SyncQueues.Queues[fixtures.QueueName]
|
||||
assert.Equal(t, fixtures.FullyPopulatedQueue, actualQueue)
|
||||
}
|
||||
|
||||
func TestCreateQueueV1_success_with_redrive_policy(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
dupe, _ := copystructure.Copy(fixtures.CreateQueueRequest)
|
||||
c, _ := dupe.(models.CreateQueueRequest)
|
||||
c.Attributes.RedrivePolicy = models.RedrivePolicy{
|
||||
MaxReceiveCount: 100,
|
||||
DeadLetterTargetArn: fmt.Sprintf("arn:aws:sqs:us-east-1:100010001000:%s", fixtures.DeadLetterQueueName),
|
||||
}
|
||||
|
||||
v := resultingStruct.(*models.CreateQueueRequest)
|
||||
*v = c
|
||||
return true
|
||||
}
|
||||
|
||||
dlq := &models.Queue{
|
||||
Name: fixtures.DeadLetterQueueName,
|
||||
}
|
||||
models.SyncQueues.Queues[fixtures.DeadLetterQueueName] = dlq
|
||||
|
||||
expectedQueue := &models.Queue{
|
||||
Name: fixtures.QueueName,
|
||||
URL: fmt.Sprintf("http://%s.%s:%s/%s/%s",
|
||||
fixtures.LOCAL_ENVIRONMENT.Region,
|
||||
fixtures.LOCAL_ENVIRONMENT.Host,
|
||||
fixtures.LOCAL_ENVIRONMENT.Port,
|
||||
fixtures.LOCAL_ENVIRONMENT.AccountID,
|
||||
fixtures.QueueName,
|
||||
),
|
||||
Arn: fmt.Sprintf("arn:aws:sqs:%s:%s:%s",
|
||||
fixtures.LOCAL_ENVIRONMENT.Region,
|
||||
fixtures.LOCAL_ENVIRONMENT.AccountID,
|
||||
fixtures.QueueName,
|
||||
),
|
||||
VisibilityTimeout: 5,
|
||||
ReceiveMessageWaitTimeSeconds: 4,
|
||||
DelaySeconds: 1,
|
||||
MaximumMessageSize: 2,
|
||||
MessageRetentionPeriod: 3,
|
||||
DeadLetterQueue: dlq,
|
||||
MaxReceiveCount: 100,
|
||||
Duplicates: make(map[string]time.Time),
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := CreateQueueV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, fixtures.CreateQueueResponse, response)
|
||||
|
||||
actualQueue := models.SyncQueues.Queues[fixtures.QueueName]
|
||||
assert.Equal(t, expectedQueue, actualQueue)
|
||||
}
|
||||
|
||||
func TestCreateQueueV1_success_with_existing_queue(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.CreateQueueRequest)
|
||||
*v = fixtures.CreateQueueRequest
|
||||
return true
|
||||
}
|
||||
|
||||
q := &models.Queue{
|
||||
Name: fixtures.QueueName,
|
||||
}
|
||||
models.SyncQueues.Queues[fixtures.QueueName] = q
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := CreateQueueV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, fixtures.CreateQueueResponse, response)
|
||||
|
||||
actualQueue := models.SyncQueues.Queues[fixtures.QueueName]
|
||||
assert.Equal(t, q, actualQueue)
|
||||
}
|
||||
|
||||
func TestCreateQueueV1_success_with_no_request_attributes_falls_back_to_default(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
dupe, _ := copystructure.Copy(fixtures.CreateQueueRequest)
|
||||
c, _ := dupe.(models.CreateQueueRequest)
|
||||
c.Attributes = models.QueueAttributes{}
|
||||
|
||||
v := resultingStruct.(*models.CreateQueueRequest)
|
||||
*v = c
|
||||
return true
|
||||
}
|
||||
|
||||
expectedQueue := &models.Queue{
|
||||
Name: fixtures.QueueName,
|
||||
URL: fmt.Sprintf("http://%s.%s:%s/%s/%s",
|
||||
fixtures.LOCAL_ENVIRONMENT.Region,
|
||||
fixtures.LOCAL_ENVIRONMENT.Host,
|
||||
fixtures.LOCAL_ENVIRONMENT.Port,
|
||||
fixtures.LOCAL_ENVIRONMENT.AccountID,
|
||||
fixtures.QueueName,
|
||||
),
|
||||
Arn: fmt.Sprintf("arn:aws:sqs:%s:%s:%s",
|
||||
fixtures.LOCAL_ENVIRONMENT.Region,
|
||||
fixtures.LOCAL_ENVIRONMENT.AccountID,
|
||||
fixtures.QueueName,
|
||||
),
|
||||
VisibilityTimeout: 0,
|
||||
ReceiveMessageWaitTimeSeconds: 0,
|
||||
DelaySeconds: 0,
|
||||
MaximumMessageSize: 0,
|
||||
MessageRetentionPeriod: 0,
|
||||
Duplicates: make(map[string]time.Time),
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := CreateQueueV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, fixtures.CreateQueueResponse, response)
|
||||
|
||||
actualQueue := models.SyncQueues.Queues[fixtures.QueueName]
|
||||
assert.Equal(t, expectedQueue, actualQueue)
|
||||
}
|
||||
|
||||
func TestCreateQueueV1_success_no_configured_region_for_queue_url(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
models.CurrentEnvironment.Region = ""
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
dupe, _ := copystructure.Copy(fixtures.CreateQueueRequest)
|
||||
c, _ := dupe.(models.CreateQueueRequest)
|
||||
c.Attributes = models.QueueAttributes{}
|
||||
|
||||
v := resultingStruct.(*models.CreateQueueRequest)
|
||||
*v = c
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, _ := CreateQueueV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
|
||||
actualQueue := models.SyncQueues.Queues[fixtures.QueueName]
|
||||
assert.Equal(t,
|
||||
fmt.Sprintf("http://%s:%s/%s/%s",
|
||||
fixtures.LOCAL_ENVIRONMENT.Host,
|
||||
fixtures.LOCAL_ENVIRONMENT.Port,
|
||||
fixtures.LOCAL_ENVIRONMENT.AccountID,
|
||||
fixtures.QueueName,
|
||||
),
|
||||
actualQueue.URL,
|
||||
)
|
||||
}
|
||||
|
||||
func TestCreateQueueV1_request_transformer_error(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, _ := CreateQueueV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
}
|
||||
|
||||
func TestCreateQueueV1_invalid_dead_letter_queue_error(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
dupe, _ := copystructure.Copy(fixtures.CreateQueueRequest)
|
||||
c, _ := dupe.(models.CreateQueueRequest)
|
||||
c.Attributes.RedrivePolicy = models.RedrivePolicy{
|
||||
MaxReceiveCount: 100,
|
||||
DeadLetterTargetArn: fmt.Sprintf("arn:aws:sqs:us-east-1:100010001000:%s", "garbage"),
|
||||
}
|
||||
|
||||
v := resultingStruct.(*models.CreateQueueRequest)
|
||||
*v = c
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, _ := CreateQueueV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func DeleteMessageV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
|
||||
requestBody := models.NewDeleteMessageRequest()
|
||||
ok := utils.REQUEST_TRANSFORMER(requestBody, req, false)
|
||||
if !ok {
|
||||
log.Error("Invalid Request - DeleteMessageV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
|
||||
// Retrieve FormValues required
|
||||
receiptHandle := requestBody.ReceiptHandle
|
||||
|
||||
// Retrieve FormValues required
|
||||
queueUrl := requestBody.QueueUrl
|
||||
queueName := ""
|
||||
if queueUrl == "" {
|
||||
vars := mux.Vars(req)
|
||||
queueName = vars["queueName"]
|
||||
} else {
|
||||
uriSegments := strings.Split(queueUrl, "/")
|
||||
queueName = uriSegments[len(uriSegments)-1]
|
||||
}
|
||||
|
||||
log.Info("Deleting Message, Queue:", queueName, ", ReceiptHandle:", receiptHandle)
|
||||
|
||||
// Find queue/message with the receipt handle and delete
|
||||
models.SyncQueues.Lock()
|
||||
defer models.SyncQueues.Unlock()
|
||||
if _, ok := models.SyncQueues.Queues[queueName]; ok {
|
||||
for i, msg := range models.SyncQueues.Queues[queueName].Messages {
|
||||
if msg.ReceiptHandle == receiptHandle {
|
||||
// Unlock messages for the group
|
||||
log.Debugf("FIFO Queue %s unlocking group %s:", queueName, msg.GroupID)
|
||||
models.SyncQueues.Queues[queueName].UnlockGroup(msg.GroupID)
|
||||
//Delete message from Q
|
||||
models.SyncQueues.Queues[queueName].Messages = append(models.SyncQueues.Queues[queueName].Messages[:i], models.SyncQueues.Queues[queueName].Messages[i+1:]...)
|
||||
delete(models.SyncQueues.Queues[queueName].Duplicates, msg.DeduplicationID)
|
||||
|
||||
// Create, encode/xml and send response
|
||||
respStruct := models.DeleteMessageResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
return 200, &respStruct
|
||||
}
|
||||
}
|
||||
log.Warning("Receipt Handle not found")
|
||||
} else {
|
||||
log.Warning("Queue not found")
|
||||
}
|
||||
|
||||
return utils.CreateErrorResponseV1("MessageDoesNotExist", true)
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func DeleteMessageBatchV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
|
||||
requestBody := models.NewDeleteMessageBatchRequest()
|
||||
ok := utils.REQUEST_TRANSFORMER(requestBody, req, false)
|
||||
if !ok {
|
||||
log.Error("Invalid Request - DeleteMessageBatchV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
|
||||
queueUrl := requestBody.QueueUrl
|
||||
|
||||
queueName := ""
|
||||
if queueUrl == "" {
|
||||
vars := mux.Vars(req)
|
||||
queueName = vars["queueName"]
|
||||
} else {
|
||||
uriSegments := strings.Split(queueUrl, "/")
|
||||
queueName = uriSegments[len(uriSegments)-1]
|
||||
}
|
||||
|
||||
if _, ok := models.SyncQueues.Queues[queueName]; !ok {
|
||||
return utils.CreateErrorResponseV1("QueueNotFound", true)
|
||||
}
|
||||
|
||||
if len(requestBody.Entries) == 0 {
|
||||
return utils.CreateErrorResponseV1("EmptyBatchRequest", true)
|
||||
}
|
||||
|
||||
if len(requestBody.Entries) > 10 {
|
||||
return utils.CreateErrorResponseV1("TooManyEntriesInBatchRequest", true)
|
||||
}
|
||||
|
||||
ids := map[string]bool{}
|
||||
for _, v := range requestBody.Entries {
|
||||
if _, found := ids[v.Id]; found {
|
||||
return utils.CreateErrorResponseV1("BatchEntryIdsNotDistinct", true)
|
||||
}
|
||||
ids[v.Id] = true
|
||||
}
|
||||
|
||||
models.SyncQueues.Lock()
|
||||
defer models.SyncQueues.Unlock()
|
||||
|
||||
// create deleteMessageMap
|
||||
deleteMessageMap := make(map[string]*deleteEntry)
|
||||
for _, entry := range requestBody.Entries {
|
||||
deleteMessageMap[entry.ReceiptHandle] = &deleteEntry{
|
||||
Id: entry.Id,
|
||||
ReceiptHandle: entry.ReceiptHandle,
|
||||
Deleted: false,
|
||||
}
|
||||
}
|
||||
|
||||
deletedEntries := make([]models.DeleteMessageBatchResultEntry, 0)
|
||||
// create a slice to hold messages that are not deleted
|
||||
remainingMessages := make([]models.SqsMessage, 0, len(models.SyncQueues.Queues[queueName].Messages))
|
||||
|
||||
// delete message from queue
|
||||
for _, message := range models.SyncQueues.Queues[queueName].Messages {
|
||||
if deleteEntry, found := deleteMessageMap[message.ReceiptHandle]; found {
|
||||
// Unlock messages for the group
|
||||
log.Debugf("FIFO Queue %s unlocking group %s:", queueName, message.GroupID)
|
||||
models.SyncQueues.Queues[queueName].UnlockGroup(message.GroupID)
|
||||
delete(models.SyncQueues.Queues[queueName].Duplicates, message.DeduplicationID)
|
||||
deleteEntry.Deleted = true
|
||||
deletedEntries = append(deletedEntries, models.DeleteMessageBatchResultEntry{Id: deleteEntry.Id})
|
||||
} else {
|
||||
remainingMessages = append(remainingMessages, message)
|
||||
}
|
||||
}
|
||||
|
||||
// Update the queue with the remaining mesages
|
||||
models.SyncQueues.Queues[queueName].Messages = remainingMessages
|
||||
|
||||
// Process not found entries
|
||||
notFoundEntries := make([]models.BatchResultErrorEntry, 0)
|
||||
for _, deleteEntry := range deleteMessageMap {
|
||||
if !deleteEntry.Deleted {
|
||||
notFoundEntries = append(notFoundEntries, models.BatchResultErrorEntry{
|
||||
Code: "1",
|
||||
Id: deleteEntry.Id,
|
||||
Message: "Message not found",
|
||||
SenderFault: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
respStruct := models.DeleteMessageBatchResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Result: models.DeleteMessageBatchResult{
|
||||
Successful: deletedEntries,
|
||||
Failed: notFoundEntries,
|
||||
},
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
|
||||
return http.StatusOK, respStruct
|
||||
|
||||
}
|
||||
|
||||
type deleteEntry struct {
|
||||
Id string
|
||||
ReceiptHandle string
|
||||
Error string
|
||||
Deleted bool
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"shared-sqs/app/conf"
|
||||
"shared-sqs/app/fixtures"
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/test"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDeleteMessageBatchV1_success_all_message(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
q := &models.Queue{
|
||||
Name: "testing",
|
||||
Messages: []models.SqsMessage{
|
||||
{
|
||||
MessageBody: "test%20message%20body%201",
|
||||
ReceiptHandle: "test1",
|
||||
},
|
||||
{
|
||||
MessageBody: "test%20message%20body%202",
|
||||
ReceiptHandle: "test2",
|
||||
},
|
||||
{
|
||||
MessageBody: "test%20message%20body%203",
|
||||
ReceiptHandle: "test3",
|
||||
},
|
||||
},
|
||||
}
|
||||
models.SyncQueues.Queues["testing"] = q
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.DeleteMessageBatchRequest)
|
||||
*v = models.DeleteMessageBatchRequest{
|
||||
Entries: []models.DeleteMessageBatchRequestEntry{
|
||||
{
|
||||
Id: "delete-test-1",
|
||||
ReceiptHandle: "test1",
|
||||
},
|
||||
{
|
||||
Id: "delete-test-2",
|
||||
ReceiptHandle: "test2",
|
||||
},
|
||||
{
|
||||
Id: "delete-test-3",
|
||||
ReceiptHandle: "test3",
|
||||
},
|
||||
},
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "testing"),
|
||||
}
|
||||
return true
|
||||
}
|
||||
_, request2 := test.GenerateRequestInfo(
|
||||
"POST",
|
||||
"/",
|
||||
nil,
|
||||
true)
|
||||
|
||||
status, response2 := DeleteMessageBatchV1(request2)
|
||||
deleteMessageBatchResponse := response2.(models.DeleteMessageBatchResponse)
|
||||
assert.Equal(t, status, http.StatusOK)
|
||||
assert.Equal(t, "delete-test-1", deleteMessageBatchResponse.Result.Successful[0].Id)
|
||||
assert.Equal(t, "delete-test-2", deleteMessageBatchResponse.Result.Successful[1].Id)
|
||||
assert.Equal(t, "delete-test-3", deleteMessageBatchResponse.Result.Successful[2].Id)
|
||||
assert.Empty(t, deleteMessageBatchResponse.Result.Failed)
|
||||
assert.Empty(t, models.SyncQueues.Queues["testing"].Messages)
|
||||
}
|
||||
func TestDeleteMessageBatchV1_success_not_found_message(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
q := &models.Queue{
|
||||
Name: "testing",
|
||||
Messages: []models.SqsMessage{
|
||||
{
|
||||
MessageBody: "test%20message%20body%201",
|
||||
ReceiptHandle: "test1",
|
||||
},
|
||||
{
|
||||
MessageBody: "test%20message%20body%203",
|
||||
ReceiptHandle: "test3",
|
||||
},
|
||||
},
|
||||
}
|
||||
models.SyncQueues.Queues["testing"] = q
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.DeleteMessageBatchRequest)
|
||||
*v = models.DeleteMessageBatchRequest{
|
||||
Entries: []models.DeleteMessageBatchRequestEntry{
|
||||
{
|
||||
Id: "delete-test-1",
|
||||
ReceiptHandle: "test1",
|
||||
},
|
||||
{
|
||||
Id: "delete-test-2",
|
||||
ReceiptHandle: "test2",
|
||||
},
|
||||
{
|
||||
Id: "delete-test-3",
|
||||
ReceiptHandle: "test3",
|
||||
},
|
||||
},
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "testing"),
|
||||
}
|
||||
return true
|
||||
}
|
||||
_, request := test.GenerateRequestInfo(
|
||||
"POST",
|
||||
"/",
|
||||
nil,
|
||||
true)
|
||||
|
||||
status, response := DeleteMessageBatchV1(request)
|
||||
deleteMessageBatchResponse := response.(models.DeleteMessageBatchResponse)
|
||||
assert.Equal(t, status, http.StatusOK)
|
||||
assert.Equal(t, "delete-test-1", deleteMessageBatchResponse.Result.Successful[0].Id)
|
||||
assert.Equal(t, "delete-test-3", deleteMessageBatchResponse.Result.Successful[1].Id)
|
||||
assert.Equal(t, "1", deleteMessageBatchResponse.Result.Failed[0].Code)
|
||||
assert.Equal(t, "delete-test-2", deleteMessageBatchResponse.Result.Failed[0].Id)
|
||||
assert.Equal(t, "Message not found", deleteMessageBatchResponse.Result.Failed[0].Message)
|
||||
assert.True(t, deleteMessageBatchResponse.Result.Failed[0].SenderFault)
|
||||
assert.Empty(t, models.SyncQueues.Queues["testing"].Messages)
|
||||
}
|
||||
|
||||
func TestDeleteMessageBatchV1_error_not_found_queue(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.DeleteMessageBatchRequest)
|
||||
*v = models.DeleteMessageBatchRequest{
|
||||
Entries: []models.DeleteMessageBatchRequestEntry{
|
||||
{
|
||||
Id: "delete-test-1",
|
||||
ReceiptHandle: "test1",
|
||||
},
|
||||
{
|
||||
Id: "delete-test-2",
|
||||
ReceiptHandle: "test2",
|
||||
},
|
||||
{
|
||||
Id: "delete-test-3",
|
||||
ReceiptHandle: "test3",
|
||||
},
|
||||
},
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "not-exist-queue"),
|
||||
}
|
||||
return true
|
||||
}
|
||||
_, r := test.GenerateRequestInfo(
|
||||
"POST",
|
||||
"/",
|
||||
nil,
|
||||
true)
|
||||
|
||||
status, _ := DeleteMessageBatchV1(r)
|
||||
assert.Equal(t, status, http.StatusBadRequest)
|
||||
|
||||
}
|
||||
|
||||
func TestDeleteMessageBatchV1_error_no_entry(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.DeleteMessageBatchRequest)
|
||||
*v = models.DeleteMessageBatchRequest{
|
||||
Entries: make([]models.DeleteMessageBatchRequestEntry, 0),
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "unit-queue1"),
|
||||
}
|
||||
return true
|
||||
}
|
||||
_, r := test.GenerateRequestInfo(
|
||||
"POST",
|
||||
"/",
|
||||
nil,
|
||||
true)
|
||||
|
||||
status, _ := DeleteMessageBatchV1(r)
|
||||
assert.Equal(t, status, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func TestDeleteMessageBatchV1_error_too_many_entries(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.DeleteMessageBatchRequest)
|
||||
*v = models.DeleteMessageBatchRequest{
|
||||
Entries: []models.DeleteMessageBatchRequestEntry{
|
||||
{
|
||||
Id: "test-1",
|
||||
ReceiptHandle: "test-1",
|
||||
},
|
||||
{
|
||||
Id: "test-2",
|
||||
ReceiptHandle: "test-2",
|
||||
},
|
||||
{
|
||||
Id: "test-3",
|
||||
ReceiptHandle: "test-3",
|
||||
},
|
||||
{
|
||||
Id: "test-4",
|
||||
ReceiptHandle: "test-4",
|
||||
},
|
||||
{
|
||||
Id: "test-5",
|
||||
ReceiptHandle: "test-5",
|
||||
},
|
||||
{
|
||||
Id: "test-6",
|
||||
ReceiptHandle: "test-6",
|
||||
},
|
||||
{
|
||||
Id: "test-7",
|
||||
ReceiptHandle: "test-7",
|
||||
},
|
||||
{
|
||||
Id: "test-8",
|
||||
ReceiptHandle: "test-8",
|
||||
},
|
||||
{
|
||||
Id: "test-9",
|
||||
ReceiptHandle: "test-9",
|
||||
},
|
||||
{
|
||||
Id: "test-10",
|
||||
ReceiptHandle: "test-10",
|
||||
},
|
||||
{
|
||||
Id: "test-11",
|
||||
ReceiptHandle: "test-11",
|
||||
},
|
||||
},
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "unit-queue1"),
|
||||
}
|
||||
return true
|
||||
}
|
||||
_, r := test.GenerateRequestInfo(
|
||||
"POST",
|
||||
"/",
|
||||
nil,
|
||||
true)
|
||||
|
||||
status, _ := DeleteMessageBatchV1(r)
|
||||
assert.Equal(t, status, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func TestDeleteMessageBatchV1_Error_IdNotDistinct(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.DeleteMessageBatchRequest)
|
||||
*v = models.DeleteMessageBatchRequest{
|
||||
Entries: []models.DeleteMessageBatchRequestEntry{
|
||||
{
|
||||
Id: "delete-test-1",
|
||||
ReceiptHandle: "test1",
|
||||
},
|
||||
{
|
||||
Id: "delete-test-1",
|
||||
ReceiptHandle: "test2",
|
||||
},
|
||||
},
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "unit-queue1"),
|
||||
}
|
||||
return true
|
||||
}
|
||||
_, r := test.GenerateRequestInfo(
|
||||
"POST",
|
||||
"/",
|
||||
nil,
|
||||
true)
|
||||
|
||||
status, _ := DeleteMessageBatchV1(r)
|
||||
assert.Equal(t, http.StatusBadRequest, status)
|
||||
}
|
||||
|
||||
func TestDeleteMessageBatchV1_Error_transformer(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
status, _ := DeleteMessageBatchV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, status)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"shared-sqs/app/test"
|
||||
|
||||
"shared-sqs/app/fixtures"
|
||||
"shared-sqs/app/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDeleteMessage(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
}()
|
||||
|
||||
q := &models.Queue{
|
||||
Name: "testing",
|
||||
Messages: []models.SqsMessage{{
|
||||
MessageBody: "test1",
|
||||
ReceiptHandle: "123",
|
||||
}},
|
||||
}
|
||||
|
||||
models.SyncQueues.Queues["testing"] = q
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", models.DeleteMessageRequest{
|
||||
QueueUrl: "http://localhost:4100/queue/testing",
|
||||
ReceiptHandle: "123",
|
||||
}, true)
|
||||
status, _ := DeleteMessageV1(r)
|
||||
|
||||
assert.Equal(t, status, http.StatusOK)
|
||||
assert.Empty(t, q.Messages)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"shared-sqs/app/interfaces"
|
||||
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func DeleteQueueV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
|
||||
requestBody := models.NewDeleteQueueRequest()
|
||||
ok := utils.REQUEST_TRANSFORMER(requestBody, req, false)
|
||||
if !ok {
|
||||
log.Error("Invalid Request - DeleteQueueV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
|
||||
uriSegments := strings.Split(requestBody.QueueUrl, "/")
|
||||
queueName := uriSegments[len(uriSegments)-1]
|
||||
|
||||
log.Infof("Deleting Queue: %s", queueName)
|
||||
|
||||
models.SyncQueues.Lock()
|
||||
delete(models.SyncQueues.Queues, queueName)
|
||||
models.SyncQueues.Unlock()
|
||||
|
||||
respStruct := models.DeleteQueueResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
return http.StatusOK, respStruct
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"shared-sqs/app/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"shared-sqs/app/conf"
|
||||
"shared-sqs/app/fixtures"
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
)
|
||||
|
||||
func TestDeleteQueueV1_success(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.DeleteQueueRequest)
|
||||
*v = models.DeleteQueueRequest{
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "unit-queue1"),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
expectedResponse := models.DeleteQueueResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := DeleteQueueV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, expectedResponse, response)
|
||||
|
||||
_, ok := models.SyncQueues.Queues["unit-queue1"]
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestDeleteQueueV1_success_unknown_queue(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.DeleteQueueRequest)
|
||||
*v = models.DeleteQueueRequest{
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "unknown-queue1"),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
expectedResponse := models.DeleteQueueResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := DeleteQueueV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, expectedResponse, response)
|
||||
}
|
||||
|
||||
func TestDeleteQueueV1_error_invalid_request(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, _ := DeleteQueueV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/mitchellh/copystructure"
|
||||
|
||||
"shared-sqs/app/interfaces"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func GetQueueAttributesV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
|
||||
requestBody := models.NewGetQueueAttributesRequest()
|
||||
ok := utils.REQUEST_TRANSFORMER(requestBody, req, false)
|
||||
if !ok {
|
||||
log.Error("Invalid Request - GetQueueAttributesV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
if requestBody.QueueUrl == "" {
|
||||
log.Error("Missing QueueUrl - GetQueueAttributesV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
|
||||
requestedAttributes := func() map[string]bool {
|
||||
attrs := map[string]bool{}
|
||||
if len(requestBody.AttributeNames) == 0 {
|
||||
return map[string]bool{"All": true}
|
||||
}
|
||||
for _, attr := range requestBody.AttributeNames {
|
||||
if "All" == attr {
|
||||
return map[string]bool{"All": true}
|
||||
}
|
||||
attrs[attr] = true
|
||||
}
|
||||
return attrs
|
||||
}()
|
||||
|
||||
dupe, _ := copystructure.Copy(models.AvailableQueueAttributes)
|
||||
includedAttributes, _ := dupe.(map[string]bool)
|
||||
_, ok = requestedAttributes["All"]
|
||||
if !ok {
|
||||
for attr, _ := range includedAttributes {
|
||||
_, ok := requestedAttributes[attr]
|
||||
if !ok {
|
||||
delete(includedAttributes, attr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uriSegments := strings.Split(requestBody.QueueUrl, "/")
|
||||
queueName := uriSegments[len(uriSegments)-1]
|
||||
|
||||
log.Infof("Get Queue QueueAttributes: %s", queueName)
|
||||
queueAttributes := make([]models.Attribute, 0, 0)
|
||||
|
||||
models.SyncQueues.RLock()
|
||||
defer models.SyncQueues.RUnlock()
|
||||
queue, ok := models.SyncQueues.Queues[queueName]
|
||||
if !ok {
|
||||
log.Errorf("Get Queue URL: %s queue does not exist!!!", queueName)
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
|
||||
if _, ok := includedAttributes["DelaySeconds"]; ok {
|
||||
attr := models.Attribute{Name: "DelaySeconds", Value: strconv.Itoa(queue.DelaySeconds)}
|
||||
queueAttributes = append(queueAttributes, attr)
|
||||
}
|
||||
if _, ok := includedAttributes["MaximumMessageSize"]; ok {
|
||||
attr := models.Attribute{Name: "MaximumMessageSize", Value: strconv.Itoa(queue.MaximumMessageSize)}
|
||||
queueAttributes = append(queueAttributes, attr)
|
||||
}
|
||||
if _, ok := includedAttributes["MessageRetentionPeriod"]; ok {
|
||||
attr := models.Attribute{Name: "MessageRetentionPeriod", Value: strconv.Itoa(queue.MessageRetentionPeriod)}
|
||||
queueAttributes = append(queueAttributes, attr)
|
||||
}
|
||||
if _, ok := includedAttributes["ReceiveMessageWaitTimeSeconds"]; ok {
|
||||
attr := models.Attribute{Name: "ReceiveMessageWaitTimeSeconds", Value: strconv.Itoa(queue.ReceiveMessageWaitTimeSeconds)}
|
||||
queueAttributes = append(queueAttributes, attr)
|
||||
}
|
||||
if _, ok := includedAttributes["VisibilityTimeout"]; ok {
|
||||
attr := models.Attribute{Name: "VisibilityTimeout", Value: strconv.Itoa(queue.VisibilityTimeout)}
|
||||
queueAttributes = append(queueAttributes, attr)
|
||||
}
|
||||
if _, ok := includedAttributes["ApproximateNumberOfMessages"]; ok {
|
||||
attr := models.Attribute{Name: "ApproximateNumberOfMessages", Value: strconv.Itoa(len(queue.Messages))}
|
||||
queueAttributes = append(queueAttributes, attr)
|
||||
}
|
||||
// TODO - implement
|
||||
//if _, ok := includedAttributes["ApproximateNumberOfMessagesDelayed"]; ok {
|
||||
// attr := models.Attribute{Name: "ApproximateNumberOfMessagesDelayed", Value: strconv.Itoa(len(queue.Messages))}
|
||||
// queueAttributes = append(queueAttributes, attr)
|
||||
//}
|
||||
if _, ok := includedAttributes["ApproximateNumberOfMessagesNotVisible"]; ok {
|
||||
attr := models.Attribute{Name: "ApproximateNumberOfMessagesNotVisible", Value: strconv.Itoa(numberOfHiddenMessagesInQueue(*queue))}
|
||||
queueAttributes = append(queueAttributes, attr)
|
||||
}
|
||||
if _, ok := includedAttributes["CreatedTimestamp"]; ok {
|
||||
attr := models.Attribute{Name: "CreatedTimestamp", Value: "0000000000"}
|
||||
queueAttributes = append(queueAttributes, attr)
|
||||
}
|
||||
if _, ok := includedAttributes["LastModifiedTimestamp"]; ok {
|
||||
attr := models.Attribute{Name: "LastModifiedTimestamp", Value: "0000000000"}
|
||||
queueAttributes = append(queueAttributes, attr)
|
||||
}
|
||||
if _, ok := includedAttributes["QueueArn"]; ok {
|
||||
attr := models.Attribute{Name: "QueueArn", Value: queue.Arn}
|
||||
queueAttributes = append(queueAttributes, attr)
|
||||
}
|
||||
// TODO - implement
|
||||
//if _, ok := includedAttributes["Policy"]; ok {
|
||||
// attr := models.Attribute{Name: "Policy", Value: ""}
|
||||
// queueAttributes = append(queueAttributes, attr)
|
||||
//}
|
||||
//if _, ok := includedAttributes["RedriveAllowPolicy"]; ok {
|
||||
// attr := models.Attribute{Name: "RedriveAllowPolicy", Value: ""}
|
||||
// queueAttributes = append(queueAttributes, attr)
|
||||
//}
|
||||
if _, ok := includedAttributes["RedrivePolicy"]; ok && queue.DeadLetterQueue != nil {
|
||||
attr := models.Attribute{Name: "RedrivePolicy", Value: fmt.Sprintf(`{"maxReceiveCount":"%d", "deadLetterTargetArn":"%s"}`, queue.MaxReceiveCount, queue.DeadLetterQueue.Arn)}
|
||||
queueAttributes = append(queueAttributes, attr)
|
||||
}
|
||||
|
||||
respStruct := models.GetQueueAttributesResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Result: models.GetQueueAttributesResult{Attrs: queueAttributes},
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
return http.StatusOK, respStruct
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"shared-sqs/app/test"
|
||||
|
||||
"github.com/mitchellh/copystructure"
|
||||
|
||||
"shared-sqs/app/conf"
|
||||
|
||||
"shared-sqs/app/fixtures"
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetQueueAttributesV1_success_all(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.GetQueueAttributesRequest)
|
||||
*v = fixtures.GetQueueAttributesRequest
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := GetQueueAttributesV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, fixtures.GetQueueAttributesResponse, response)
|
||||
}
|
||||
|
||||
func TestGetQueueAttributesV1_success_no_request_attrs_returns_all(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.GetQueueAttributesRequest)
|
||||
*v = models.GetQueueAttributesRequest{
|
||||
QueueUrl: "unit-queue1",
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := GetQueueAttributesV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, fixtures.GetQueueAttributesResponse, response)
|
||||
}
|
||||
|
||||
func TestGetQueueAttributesV1_success_all_with_redrive_queue(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.GetQueueAttributesRequest)
|
||||
*v = models.GetQueueAttributesRequest{
|
||||
QueueUrl: "unit-queue2",
|
||||
AttributeNames: []string{"All"},
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := GetQueueAttributesV1(r)
|
||||
|
||||
dupe, _ := copystructure.Copy(fixtures.GetQueueAttributesResponse)
|
||||
expectedResponse, _ := dupe.(models.GetQueueAttributesResponse)
|
||||
expectedResponse.Result.Attrs[9].Value = fmt.Sprintf("%s:%s", fixtures.BASE_SQS_ARN, "unit-queue2")
|
||||
expectedResponse.Result.Attrs = append(expectedResponse.Result.Attrs,
|
||||
models.Attribute{
|
||||
Name: "RedrivePolicy",
|
||||
Value: fmt.Sprintf(`{"maxReceiveCount":"1", "deadLetterTargetArn":"%s:%s"}`, fixtures.BASE_SQS_ARN, "dead-letter-queue1"),
|
||||
},
|
||||
)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, expectedResponse, response)
|
||||
}
|
||||
|
||||
func TestGetQueueAttributesV1_success_specific_fields(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.GetQueueAttributesRequest)
|
||||
*v = models.GetQueueAttributesRequest{
|
||||
QueueUrl: fmt.Sprintf("%s/unit-queue1", fixtures.BASE_URL),
|
||||
AttributeNames: []string{"DelaySeconds"},
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := GetQueueAttributesV1(r)
|
||||
|
||||
expectedResponse := models.GetQueueAttributesResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Result: models.GetQueueAttributesResult{Attrs: []models.Attribute{
|
||||
models.Attribute{
|
||||
Name: "DelaySeconds",
|
||||
Value: "0",
|
||||
},
|
||||
}},
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, expectedResponse, response)
|
||||
}
|
||||
|
||||
func TestGetQueueAttributesV1_request_transformer_error(t *testing.T) {
|
||||
defer func() {
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, _ := GetQueueAttributesV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
}
|
||||
|
||||
func TestGetQueueAttributesV1_missing_queue_url_in_request_returns_error(t *testing.T) {
|
||||
defer func() {
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.GetQueueAttributesRequest)
|
||||
*v = models.GetQueueAttributesRequest{
|
||||
QueueUrl: "",
|
||||
AttributeNames: []string{},
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, _ := GetQueueAttributesV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
}
|
||||
|
||||
func TestGetQueueAttributesV1_missing_queue_returns_error(t *testing.T) {
|
||||
defer func() {
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.GetQueueAttributesRequest)
|
||||
*v = fixtures.GetQueueAttributesRequest
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, _ := GetQueueAttributesV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func GetQueueUrlV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
|
||||
requestBody := models.NewGetQueueUrlRequest()
|
||||
ok := utils.REQUEST_TRANSFORMER(requestBody, req, false)
|
||||
if !ok {
|
||||
log.Error("Invalid Request - GetQueueUrlV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
|
||||
queueName := requestBody.QueueName
|
||||
if _, ok := models.SyncQueues.Queues[queueName]; !ok {
|
||||
log.Error("Get Queue URL:", queueName, ", queue does not exist!!!")
|
||||
return utils.CreateErrorResponseV1("QueueNotFound", true)
|
||||
}
|
||||
|
||||
queue := models.SyncQueues.Queues[queueName]
|
||||
log.Debug("Get Queue URL:", queue.Name)
|
||||
|
||||
result := models.GetQueueUrlResult{QueueUrl: queue.URL}
|
||||
respStruct := models.GetQueueUrlResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Result: result,
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
return http.StatusOK, respStruct
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"shared-sqs/app/test"
|
||||
|
||||
"shared-sqs/app/conf"
|
||||
"shared-sqs/app/fixtures"
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetQueueUrlV1_success(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.GetQueueUrlRequest)
|
||||
*v = models.GetQueueUrlRequest{
|
||||
QueueName: "unit-queue1",
|
||||
QueueOwnerAWSAccountId: "fugafuga",
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo(
|
||||
"POST",
|
||||
"/",
|
||||
nil,
|
||||
true)
|
||||
code, response := GetQueueUrlV1(r)
|
||||
|
||||
get_queue_url_response := response.(models.GetQueueUrlResponse)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Contains(t, get_queue_url_response.Result.QueueUrl, fmt.Sprintf("%s/%s", fixtures.BASE_URL, "unit-queue1"))
|
||||
|
||||
}
|
||||
|
||||
func TestGetQueueUrlV1_error_no_queue(t *testing.T) {
|
||||
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.GetQueueUrlRequest)
|
||||
*v = models.GetQueueUrlRequest{
|
||||
QueueName: "not-exist-unit-queue1",
|
||||
QueueOwnerAWSAccountId: "fugafuga",
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo(
|
||||
"POST",
|
||||
"/",
|
||||
nil,
|
||||
true)
|
||||
code, response := GetQueueUrlV1(r)
|
||||
|
||||
expected := models.ErrorResult{
|
||||
Type: "Not Found",
|
||||
Code: "AWS.SimpleQueueService.NonExistentQueue",
|
||||
Message: "The specified queue does not exist for this wsdl version.",
|
||||
}
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
assert.Equal(t, response.GetResult().(models.ErrorResult), expected)
|
||||
}
|
||||
|
||||
func TestGetQueueUrlV1_error_request_transformer(t *testing.T) {
|
||||
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo(
|
||||
"POST",
|
||||
"/",
|
||||
nil,
|
||||
true)
|
||||
code, _ := GetQueueUrlV1(r)
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"shared-sqs/app/models"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func init() {
|
||||
models.SyncQueues.Queues = make(map[string]*models.Queue)
|
||||
}
|
||||
|
||||
func PeriodicTasks(d time.Duration, quit chan bool) {
|
||||
ticker := time.NewTicker(d)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
models.SyncQueues.Lock()
|
||||
for qName := range models.SyncQueues.Queues {
|
||||
queue := models.SyncQueues.Queues[qName]
|
||||
|
||||
// Reset deduplication period
|
||||
for dedupId, startTime := range queue.Duplicates {
|
||||
if time.Now().After(startTime.Add(models.DeduplicationPeriod)) {
|
||||
log.Debugf("deduplication period for message with deduplicationId [%s] expired", dedupId)
|
||||
delete(queue.Duplicates, dedupId)
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("Queue [%s] length [%d]", queue.Name, len(queue.Messages))
|
||||
for i := 0; i < len(queue.Messages); i++ {
|
||||
msg := &queue.Messages[i]
|
||||
|
||||
if msg.ReceiptHandle != "" {
|
||||
if msg.VisibilityTimeout.Before(time.Now()) {
|
||||
log.Debugf("Making message visible again %s", msg.ReceiptHandle)
|
||||
queue.UnlockGroup(msg.GroupID)
|
||||
msg.ReceiptHandle = ""
|
||||
msg.ReceiptTime = time.Now().UTC()
|
||||
msg.Retry++
|
||||
if queue.MaxReceiveCount > 0 &&
|
||||
queue.DeadLetterQueue != nil &&
|
||||
msg.Retry >= queue.MaxReceiveCount {
|
||||
queue.DeadLetterQueue.Messages = append(queue.DeadLetterQueue.Messages, *msg)
|
||||
queue.Messages = append(queue.Messages[:i], queue.Messages[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
models.SyncQueues.Unlock()
|
||||
case <-quit:
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func numberOfHiddenMessagesInQueue(queue models.Queue) int {
|
||||
num := 0
|
||||
for _, m := range queue.Messages {
|
||||
if m.ReceiptHandle != "" || m.DelaySecs > 0 && time.Now().Before(m.SentTime.Add(time.Duration(m.DelaySecs)*time.Second)) {
|
||||
num++
|
||||
}
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
func getQueueFromPath(formVal string, theUrl string) string {
|
||||
if formVal != "" {
|
||||
return formVal
|
||||
}
|
||||
u, err := url.Parse(theUrl)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return u.Path
|
||||
}
|
||||
@@ -0,0 +1,668 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"shared-sqs/app/conf"
|
||||
|
||||
"shared-sqs/app/fixtures"
|
||||
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// TODO - Admiral-Piett these are better but still screwy. It's easy to have race conditions in here, so
|
||||
// we have to name all the queues uniquely and leave them around so we're not resetting ourselves.
|
||||
// Stupid. Handle the global memory issues and this can be easily resolved.
|
||||
func Test_PeriodicTasks_deletes_deduplication_period_upon_expiration(t *testing.T) {
|
||||
models.DeduplicationPeriod = 20 * time.Millisecond
|
||||
quit := make(chan bool)
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
quit <- true
|
||||
models.DeduplicationPeriod = 5 * time.Minute
|
||||
}()
|
||||
|
||||
qName := "gosqs-dedupe-queue1"
|
||||
mainQueue := &models.Queue{
|
||||
Name: qName,
|
||||
URL: fmt.Sprintf("%s/%s", fixtures.BASE_URL, qName),
|
||||
Arn: fmt.Sprintf("%s:%s", fixtures.BASE_SQS_ARN, qName),
|
||||
Duplicates: map[string]time.Time{
|
||||
"12345": time.Now(),
|
||||
},
|
||||
}
|
||||
models.SyncQueues.Lock()
|
||||
models.SyncQueues.Queues[qName] = mainQueue
|
||||
models.SyncQueues.Unlock()
|
||||
|
||||
go PeriodicTasks(10*time.Millisecond, quit)
|
||||
|
||||
assertions := func() bool {
|
||||
models.SyncQueues.Lock()
|
||||
defer models.SyncQueues.Unlock()
|
||||
|
||||
ok := 0 == len(mainQueue.Duplicates)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
assert.Eventually(t, assertions, 10*time.Second, 10*time.Millisecond)
|
||||
}
|
||||
|
||||
func Test_PeriodicTasks_VisibilityTimeout_expires(t *testing.T) {
|
||||
quit := make(chan bool)
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
quit <- true
|
||||
}()
|
||||
qName := "gosqs-visibility-queue1"
|
||||
mainQueue := &models.Queue{
|
||||
Name: qName,
|
||||
URL: fmt.Sprintf("%s/%s", fixtures.BASE_URL, qName),
|
||||
Arn: fmt.Sprintf("%s:%s", fixtures.BASE_SQS_ARN, qName),
|
||||
}
|
||||
mainQueue.Messages = append(mainQueue.Messages, models.SqsMessage{
|
||||
MessageBody: "1",
|
||||
ReceiptHandle: "12345",
|
||||
VisibilityTimeout: time.Now().Add(30 * time.Millisecond),
|
||||
})
|
||||
|
||||
models.SyncQueues.Lock()
|
||||
models.SyncQueues.Queues[qName] = mainQueue
|
||||
models.SyncQueues.Unlock()
|
||||
|
||||
go PeriodicTasks(10*time.Millisecond, quit)
|
||||
|
||||
assertions := func() bool {
|
||||
models.SyncQueues.Lock()
|
||||
defer models.SyncQueues.Unlock()
|
||||
|
||||
ok := !mainQueue.Messages[0].ReceiptTime.IsZero()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
ok = "1" == mainQueue.Messages[0].MessageBody
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
ok = "" == mainQueue.Messages[0].ReceiptHandle
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
ok = 1 == mainQueue.Messages[0].Retry
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
assert.Eventually(t, assertions, 10*time.Second, 10*time.Millisecond)
|
||||
}
|
||||
|
||||
func Test_PeriodicTasks_moves_single_message_to_dead_letter_queue_upon_passing_receive_count(t *testing.T) {
|
||||
quit := make(chan bool)
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
quit <- true
|
||||
}()
|
||||
|
||||
qName := "gosqs-main-queue1"
|
||||
dlqName := "gosqs-dead-letter-queue1"
|
||||
dlqQueue := &models.Queue{
|
||||
Arn: fmt.Sprintf("%s/%s", fixtures.BASE_SQS_ARN, dlqName),
|
||||
Name: dlqName,
|
||||
URL: fmt.Sprintf("%s/%s", fixtures.BASE_URL, dlqName),
|
||||
}
|
||||
mainQueue := &models.Queue{
|
||||
Arn: fmt.Sprintf("%s/%s", fixtures.BASE_SQS_ARN, qName),
|
||||
DeadLetterQueue: dlqQueue,
|
||||
MaxReceiveCount: 1,
|
||||
Name: qName,
|
||||
URL: fmt.Sprintf("%s/%s", fixtures.BASE_URL, qName),
|
||||
}
|
||||
|
||||
go PeriodicTasks(10*time.Millisecond, quit)
|
||||
|
||||
models.SyncQueues.Lock()
|
||||
mainQueue.Messages = append(mainQueue.Messages, models.SqsMessage{
|
||||
MessageBody: "1",
|
||||
Retry: 100,
|
||||
ReceiptHandle: "12345",
|
||||
VisibilityTimeout: time.Now().Add(10 * time.Millisecond),
|
||||
})
|
||||
models.SyncQueues.Queues[qName] = mainQueue
|
||||
models.SyncQueues.Queues[dlqName] = dlqQueue
|
||||
models.SyncQueues.Unlock()
|
||||
|
||||
assertions := func() bool {
|
||||
models.SyncQueues.Lock()
|
||||
defer models.SyncQueues.Unlock()
|
||||
|
||||
ok := len(dlqQueue.Messages) == 1
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
ok = "1" == dlqQueue.Messages[0].MessageBody
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
assert.Eventually(t, assertions, 10*time.Second, 10*time.Millisecond)
|
||||
}
|
||||
|
||||
func Test_PeriodicTasks_moves_multiple_messages_to_dead_letter_queue_upon_passing_receive_count(t *testing.T) {
|
||||
quit := make(chan bool)
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
quit <- true
|
||||
}()
|
||||
|
||||
mainQueue := models.SyncQueues.Queues["unit-queue2"]
|
||||
dlqQueue := models.SyncQueues.Queues["dead-letter-queue1"]
|
||||
|
||||
assert.Len(t, dlqQueue.Messages, 0)
|
||||
|
||||
go PeriodicTasks(10*time.Millisecond, quit)
|
||||
|
||||
models.SyncQueues.Lock()
|
||||
mainQueue.Messages = append(mainQueue.Messages, models.SqsMessage{
|
||||
MessageBody: "1",
|
||||
Retry: 100,
|
||||
ReceiptHandle: "12345",
|
||||
})
|
||||
mainQueue.Messages = append(mainQueue.Messages, models.SqsMessage{
|
||||
MessageBody: "2",
|
||||
Retry: 100,
|
||||
ReceiptHandle: "23456",
|
||||
})
|
||||
models.SyncQueues.Unlock()
|
||||
|
||||
assertions := func() bool {
|
||||
models.SyncQueues.Lock()
|
||||
defer models.SyncQueues.Unlock()
|
||||
|
||||
ok := len(dlqQueue.Messages) == 2
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
ok = "1" == dlqQueue.Messages[0].MessageBody
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
ok = "2" == dlqQueue.Messages[1].MessageBody
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
assert.Eventually(t, assertions, 10*time.Second, 10*time.Millisecond)
|
||||
}
|
||||
|
||||
// TODO - I think all these below belong in handler tests, not in here. Double check the relevant
|
||||
// handlers for coverage and delete.
|
||||
func TestSendingAndReceivingFromFIFOQueueReturnsSameMessageOnError(t *testing.T) {
|
||||
done := make(chan bool)
|
||||
go PeriodicTasks(1*time.Second, done)
|
||||
|
||||
// create a queue
|
||||
req, err := http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
form := url.Values{}
|
||||
form.Add("Action", "CreateQueue")
|
||||
form.Add("QueueName", "requeue-reset.fifo")
|
||||
form.Add("Attribute.1.Name", "VisibilityTimeout")
|
||||
form.Add("Attribute.1.Value", "2")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, _ := CreateQueueV1(req)
|
||||
assert.Equal(t, status, http.StatusOK)
|
||||
|
||||
// send a message
|
||||
req, err = http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
form = url.Values{}
|
||||
form.Add("Action", "SendMessage")
|
||||
form.Add("QueueUrl", "http://localhost:4100/queue/requeue-reset.fifo")
|
||||
form.Add("MessageBody", "1")
|
||||
form.Add("MessageGroupId", "GROUP-X")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, _ = SendMessageV1(req)
|
||||
if status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got \n%v want %v",
|
||||
status, http.StatusOK)
|
||||
}
|
||||
|
||||
// send a message
|
||||
req, err = http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
form = url.Values{}
|
||||
form.Add("Action", "SendMessage")
|
||||
form.Add("QueueUrl", "http://localhost:4100/queue/requeue-reset.fifo")
|
||||
form.Add("MessageBody", "2")
|
||||
form.Add("MessageGroupId", "GROUP-X")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, _ = SendMessageV1(req)
|
||||
if status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got \n%v want %v",
|
||||
status, http.StatusOK)
|
||||
}
|
||||
|
||||
// receive message
|
||||
req, err = http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
form = url.Values{}
|
||||
form.Add("Action", "ReceiveMessage")
|
||||
form.Add("QueueUrl", "http://localhost:4100/queue/requeue-reset.fifo")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, resp := ReceiveMessageV1(req)
|
||||
assert.Equal(t, status, http.StatusOK)
|
||||
|
||||
result := resp.GetResult().(models.ReceiveMessageResult)
|
||||
receiptHandleFirst := result.Messages[0].ReceiptHandle
|
||||
if string(result.Messages[0].Body) != "1" {
|
||||
t.Fatalf("should have received body 1: %s", err)
|
||||
}
|
||||
|
||||
// try to receive another message and we should get none
|
||||
req, err = http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
form = url.Values{}
|
||||
form.Add("Action", "ReceiveMessage")
|
||||
form.Add("QueueUrl", "http://localhost:4100/queue/requeue-reset.fifo")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, _ = ReceiveMessageV1(req)
|
||||
assert.Equal(t, status, http.StatusOK)
|
||||
|
||||
if len(models.SyncQueues.Queues["requeue-reset.fifo"].FIFOMessages) != 1 {
|
||||
t.Fatal("there should be only 1 group locked")
|
||||
}
|
||||
|
||||
if models.SyncQueues.Queues["requeue-reset.fifo"].FIFOMessages["GROUP-X"] != 0 {
|
||||
t.Fatal("there should be GROUP-X locked")
|
||||
}
|
||||
|
||||
// remove message
|
||||
req, err = http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
form = url.Values{}
|
||||
form.Add("Action", "DeleteMessage")
|
||||
form.Add("QueueUrl", "http://localhost:4100/queue/requeue-reset.fifo")
|
||||
form.Add("ReceiptHandle", receiptHandleFirst)
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, _ = DeleteMessageV1(req)
|
||||
assert.Equal(t, status, http.StatusOK)
|
||||
|
||||
if len(models.SyncQueues.Queues["requeue-reset.fifo"].Messages) != 1 {
|
||||
t.Fatal("there should be only 1 message in queue")
|
||||
}
|
||||
|
||||
// receive message - loop until visibility timeouts
|
||||
for {
|
||||
req, err = http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
form = url.Values{}
|
||||
form.Add("Action", "ReceiveMessage")
|
||||
form.Add("QueueUrl", "http://localhost:4100/queue/requeue-reset.fifo")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, resp := ReceiveMessageV1(req)
|
||||
assert.Equal(t, status, http.StatusOK)
|
||||
|
||||
result := resp.GetResult().(models.ReceiveMessageResult)
|
||||
if len(result.Messages) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if string(result.Messages[0].Body) != "2" {
|
||||
t.Fatalf("should have received body 2: %s", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
done <- true
|
||||
}
|
||||
|
||||
func TestSendMessage_POST_DuplicatationNotAppliedToStandardQueue(t *testing.T) {
|
||||
done := make(chan bool)
|
||||
go PeriodicTasks(1*time.Second, done)
|
||||
|
||||
// create a queue
|
||||
req, err := http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
form := url.Values{}
|
||||
form.Add("Action", "CreateQueue")
|
||||
form.Add("QueueName", "stantdard-testing")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, _ := CreateQueueV1(req)
|
||||
|
||||
assert.Equal(t, status, http.StatusOK)
|
||||
|
||||
req, err = http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
form = url.Values{}
|
||||
form.Add("Action", "SendMessage")
|
||||
form.Add("QueueUrl", "http://localhost:4100/queue/stantdard-testing")
|
||||
form.Add("MessageBody", "Test1")
|
||||
form.Add("MessageDeduplicationId", "123")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, _ = SendMessageV1(req)
|
||||
|
||||
// Check the status code is what we expect.
|
||||
if status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got \n%v want %v",
|
||||
status, http.StatusOK)
|
||||
}
|
||||
if len(models.SyncQueues.Queues["stantdard-testing"].Messages) == 0 {
|
||||
t.Fatal("there should be 1 message in queue")
|
||||
}
|
||||
|
||||
form = url.Values{}
|
||||
form.Add("Action", "SendMessage")
|
||||
form.Add("QueueUrl", "http://localhost:4100/queue/stantdard-testing")
|
||||
form.Add("MessageBody", "Test2")
|
||||
form.Add("MessageDeduplicationId", "123")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, _ = SendMessageV1(req)
|
||||
|
||||
// Check the status code is what we expect.
|
||||
if status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got \n%v want %v",
|
||||
status, http.StatusOK)
|
||||
}
|
||||
if len(models.SyncQueues.Queues["stantdard-testing"].Messages) == 1 {
|
||||
t.Fatal("there should be 2 messages in queue")
|
||||
}
|
||||
done <- true
|
||||
}
|
||||
|
||||
func TestSendMessage_POST_DuplicatationDisabledOnFifoQueue(t *testing.T) {
|
||||
done := make(chan bool)
|
||||
go PeriodicTasks(1*time.Second, done)
|
||||
|
||||
// create a queue
|
||||
req, err := http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
form := url.Values{}
|
||||
form.Add("Action", "CreateQueue")
|
||||
form.Add("QueueName", "no-dup-testing.fifo")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, _ := CreateQueueV1(req)
|
||||
|
||||
assert.Equal(t, status, http.StatusOK)
|
||||
|
||||
req, err = http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
form = url.Values{}
|
||||
form.Add("Action", "SendMessage")
|
||||
form.Add("QueueUrl", "http://localhost:4100/queue/no-dup-testing.fifo")
|
||||
form.Add("MessageBody", "Test1")
|
||||
form.Add("MessageDeduplicationId", "123")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, _ = SendMessageV1(req)
|
||||
|
||||
// Check the status code is what we expect.
|
||||
if status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got \n%v want %v",
|
||||
status, http.StatusOK)
|
||||
}
|
||||
if len(models.SyncQueues.Queues["no-dup-testing.fifo"].Messages) == 0 {
|
||||
t.Fatal("there should be 1 message in queue")
|
||||
}
|
||||
|
||||
form = url.Values{}
|
||||
form.Add("Action", "SendMessage")
|
||||
form.Add("QueueUrl", "http://localhost:4100/queue/no-dup-testing.fifo")
|
||||
form.Add("MessageBody", "Test2")
|
||||
form.Add("MessageDeduplicationId", "123")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, _ = SendMessageV1(req)
|
||||
|
||||
// Check the status code is what we expect.
|
||||
if status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got \n%v want %v",
|
||||
status, http.StatusOK)
|
||||
}
|
||||
if len(models.SyncQueues.Queues["no-dup-testing.fifo"].Messages) != 2 {
|
||||
t.Fatal("there should be 2 message in queue")
|
||||
}
|
||||
done <- true
|
||||
}
|
||||
|
||||
func TestSendMessage_POST_DuplicatationEnabledOnFifoQueue(t *testing.T) {
|
||||
done := make(chan bool)
|
||||
go PeriodicTasks(1*time.Second, done)
|
||||
|
||||
// create a queue
|
||||
req, err := http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
form := url.Values{}
|
||||
form.Add("Action", "CreateQueue")
|
||||
form.Add("QueueName", "dup-testing.fifo")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, _ := CreateQueueV1(req)
|
||||
|
||||
assert.Equal(t, status, http.StatusOK)
|
||||
|
||||
req, err = http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
models.SyncQueues.Queues["dup-testing.fifo"].EnableDuplicates = true
|
||||
|
||||
form = url.Values{}
|
||||
form.Add("Action", "SendMessage")
|
||||
form.Add("QueueUrl", "http://localhost:4100/queue/dup-testing.fifo")
|
||||
form.Add("MessageBody", "Test1")
|
||||
form.Add("MessageDeduplicationId", "123")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, _ = SendMessageV1(req)
|
||||
|
||||
// Check the status code is what we expect.
|
||||
if status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got \n%v want %v",
|
||||
status, http.StatusOK)
|
||||
}
|
||||
if len(models.SyncQueues.Queues["dup-testing.fifo"].Messages) == 0 {
|
||||
t.Fatal("there should be 1 message in queue")
|
||||
}
|
||||
|
||||
form = url.Values{}
|
||||
form.Add("Action", "SendMessage")
|
||||
form.Add("QueueUrl", "http://localhost:4100/queue/dup-testing.fifo")
|
||||
form.Add("MessageBody", "Test2")
|
||||
form.Add("MessageDeduplicationId", "123")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, _ = SendMessageV1(req)
|
||||
|
||||
// Check the status code is what we expect.
|
||||
if status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got \n%v want %v",
|
||||
status, http.StatusOK)
|
||||
}
|
||||
if len(models.SyncQueues.Queues["dup-testing.fifo"].Messages) != 1 {
|
||||
t.Fatal("there should be 1 message in queue")
|
||||
}
|
||||
if body := models.SyncQueues.Queues["dup-testing.fifo"].Messages[0].MessageBody; string(body) == "Test2" {
|
||||
t.Fatal("duplicate message should not be added to queue")
|
||||
}
|
||||
done <- true
|
||||
}
|
||||
|
||||
func TestSendMessage_POST_DelaySeconds(t *testing.T) {
|
||||
// create a queue
|
||||
req, err := http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
form := url.Values{}
|
||||
form.Add("Action", "CreateQueue")
|
||||
form.Add("QueueName", "sendmessage-delay")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, _ := CreateQueueV1(req)
|
||||
|
||||
assert.Equal(t, status, http.StatusOK)
|
||||
|
||||
// send a message
|
||||
req, err = http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
form = url.Values{}
|
||||
form.Add("Action", "SendMessage")
|
||||
form.Add("QueueUrl", "http://localhost:4100/queue/sendmessage-delay")
|
||||
form.Add("MessageBody", "1")
|
||||
form.Add("DelaySeconds", "2")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
|
||||
status, _ = SendMessageV1(req)
|
||||
if status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got \n%v want %v",
|
||||
status, http.StatusOK)
|
||||
}
|
||||
|
||||
// receive message before delay is up
|
||||
req, err = http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
form = url.Values{}
|
||||
form.Add("Action", "ReceiveMessage")
|
||||
form.Add("QueueUrl", "http://localhost:4100/queue/sendmessage-delay")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
status, _ = ReceiveMessageV1(req)
|
||||
assert.Equal(t, status, http.StatusOK)
|
||||
|
||||
// receive message with wait should return after delay
|
||||
req, err = http.NewRequest("POST", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
form = url.Values{}
|
||||
form.Add("Action", "ReceiveMessage")
|
||||
form.Add("QueueUrl", "http://localhost:4100/queue/sendmessage-delay")
|
||||
form.Add("WaitTimeSeconds", "10")
|
||||
form.Add("Version", "2012-11-05")
|
||||
req.PostForm = form
|
||||
start := time.Now()
|
||||
status, _ = ReceiveMessageV1(req)
|
||||
elapsed := time.Since(start)
|
||||
assert.Equal(t, status, http.StatusOK)
|
||||
if elapsed < 1*time.Second {
|
||||
t.Errorf("handler didn't wait at all")
|
||||
}
|
||||
if elapsed > 4*time.Second {
|
||||
t.Errorf("handler didn't need to wait all WaitTimeSeconds=10, only DelaySeconds=2")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateErrorResponseV1(t *testing.T) {
|
||||
expectedResponse := models.ErrorResponse{
|
||||
Result: models.ErrorResult{
|
||||
Type: "Not Found",
|
||||
Code: "AWS.SimpleQueueService.NonExistentQueue",
|
||||
Message: "The specified queue does not exist for this wsdl version.",
|
||||
},
|
||||
RequestId: "00000000-0000-0000-0000-000000000000",
|
||||
}
|
||||
status, response := utils.CreateErrorResponseV1("QueueNotFound", true)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, status)
|
||||
assert.Equal(t, expectedResponse, response)
|
||||
}
|
||||
|
||||
// waitTimeout waits for the waitgroup for the specified max timeout.
|
||||
// Returns true if waiting timed out.
|
||||
// credits: https://stackoverflow.com/questions/32840687/timeout-for-waitgroup-wait
|
||||
func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
|
||||
c := make(chan struct{})
|
||||
go func() {
|
||||
defer close(c)
|
||||
wg.Wait()
|
||||
}()
|
||||
select {
|
||||
case <-c:
|
||||
return false // completed normally
|
||||
case <-time.After(timeout):
|
||||
return true // timed out
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"shared-sqs/app/utils"
|
||||
|
||||
"shared-sqs/app/models"
|
||||
|
||||
"shared-sqs/app/interfaces"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// TODO - set up MaxResults, NextToken request params
|
||||
//
|
||||
// https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_ListQueues.html
|
||||
func ListQueuesV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
|
||||
requestBody := models.NewListQueuesRequest()
|
||||
ok := utils.REQUEST_TRANSFORMER(requestBody, req, true)
|
||||
if !ok {
|
||||
log.Error("Invalid Request - ListQueuesV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
|
||||
log.Info("Listing Queues")
|
||||
queueUrls := make([]string, 0)
|
||||
models.SyncQueues.Lock()
|
||||
for _, queue := range models.SyncQueues.Queues {
|
||||
if strings.HasPrefix(queue.Name, requestBody.QueueNamePrefix) {
|
||||
queueUrls = append(queueUrls, queue.URL)
|
||||
}
|
||||
}
|
||||
models.SyncQueues.Unlock()
|
||||
|
||||
respStruct := models.ListQueuesResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
Result: models.ListQueuesResult{
|
||||
QueueUrls: queueUrls,
|
||||
},
|
||||
}
|
||||
|
||||
return http.StatusOK, respStruct
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"shared-sqs/app/test"
|
||||
|
||||
"shared-sqs/app/conf"
|
||||
"shared-sqs/app/fixtures"
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestListQueuesV1_success(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.ListQueueRequest)
|
||||
*v = models.ListQueueRequest{}
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := ListQueuesV1(r)
|
||||
r1 := response.(models.ListQueuesResponse)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Contains(t, r1.Result.QueueUrls, fmt.Sprintf("%s/%s", fixtures.BASE_URL, "unit-queue1"))
|
||||
assert.Contains(t, r1.Result.QueueUrls, fmt.Sprintf("%s/%s", fixtures.BASE_URL, "unit-queue2"))
|
||||
assert.Contains(t, r1.Result.QueueUrls, fmt.Sprintf("%s/%s", fixtures.BASE_URL, "dead-letter-queue1"))
|
||||
}
|
||||
|
||||
func TestListQueuesV1_success_no_queues(t *testing.T) {
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.ListQueueRequest)
|
||||
*v = models.ListQueueRequest{}
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := ListQueuesV1(r)
|
||||
r1 := response.(models.ListQueuesResponse)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, r1.Result.QueueUrls, []string{})
|
||||
}
|
||||
|
||||
func TestListQueuesV1_success_with_queue_name_prefix(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.ListQueueRequest)
|
||||
*v = models.ListQueueRequest{QueueNamePrefix: "dead-letter"}
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := ListQueuesV1(r)
|
||||
r1 := response.(models.ListQueuesResponse)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, []string{fmt.Sprintf("%s/%s", fixtures.BASE_URL, "dead-letter-queue1")}, r1.Result.QueueUrls)
|
||||
}
|
||||
|
||||
func TestListQueuesV1_success_with_queue_name_prefix_no_matching_queues(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.ListQueueRequest)
|
||||
*v = models.ListQueueRequest{QueueNamePrefix: "garbage"}
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := ListQueuesV1(r)
|
||||
r1 := response.(models.ListQueuesResponse)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, []string{}, r1.Result.QueueUrls)
|
||||
}
|
||||
|
||||
func TestListQueuesV1_request_transformer_error(t *testing.T) {
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, _ := ListQueuesV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func PurgeQueueV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
|
||||
requestBody := models.NewPurgeQueueRequest()
|
||||
ok := utils.REQUEST_TRANSFORMER(requestBody, req, false)
|
||||
if !ok {
|
||||
log.Error("Invalid Request - PurgeQueueV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
|
||||
uriSegments := strings.Split(requestBody.QueueUrl, "/")
|
||||
queueName := uriSegments[len(uriSegments)-1]
|
||||
|
||||
models.SyncQueues.Lock()
|
||||
defer models.SyncQueues.Unlock()
|
||||
if _, ok := models.SyncQueues.Queues[queueName]; !ok {
|
||||
log.Errorf("Purge Queue: %s, queue does not exist!!!", queueName)
|
||||
return utils.CreateErrorResponseV1("QueueNotFound", true)
|
||||
}
|
||||
|
||||
log.Infof("Purging Queue: %s", queueName)
|
||||
models.SyncQueues.Queues[queueName].Messages = nil
|
||||
models.SyncQueues.Queues[queueName].Duplicates = make(map[string]time.Time)
|
||||
|
||||
respStruct := models.PurgeQueueResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
return http.StatusOK, respStruct
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"shared-sqs/app/test"
|
||||
|
||||
"shared-sqs/app/conf"
|
||||
|
||||
"shared-sqs/app/fixtures"
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPurgeQueueV1_success(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.PurgeQueueRequest)
|
||||
*v = models.PurgeQueueRequest{
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "unit-queue1"),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Put a message on the queue
|
||||
targetQueue := models.SyncQueues.Queues["unit-queue1"]
|
||||
models.SyncQueues.Lock()
|
||||
targetQueue.Messages = []models.SqsMessage{models.SqsMessage{}}
|
||||
targetQueue.Duplicates = map[string]time.Time{
|
||||
"dedupe-id": time.Now(),
|
||||
}
|
||||
models.SyncQueues.Unlock()
|
||||
|
||||
expectedResponse := models.PurgeQueueResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := PurgeQueueV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, expectedResponse, response)
|
||||
|
||||
assert.Nil(t, targetQueue.Messages)
|
||||
assert.Equal(t, map[string]time.Time{}, targetQueue.Duplicates)
|
||||
}
|
||||
|
||||
func TestPurgeQueueV1_success_no_messages_on_queue(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.PurgeQueueRequest)
|
||||
*v = models.PurgeQueueRequest{
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "unit-queue1"),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
expectedResponse := models.PurgeQueueResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := PurgeQueueV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, expectedResponse, response)
|
||||
|
||||
targetQueue := models.SyncQueues.Queues["unit-queue1"]
|
||||
assert.Nil(t, targetQueue.Messages)
|
||||
assert.Equal(t, map[string]time.Time{}, targetQueue.Duplicates)
|
||||
}
|
||||
|
||||
func TestPurgeQueueV1_request_transformer_error(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, _ := PurgeQueueV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
}
|
||||
|
||||
func TestPurgeQueueV1_requested_queue_does_not_exist(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.PurgeQueueRequest)
|
||||
*v = models.PurgeQueueRequest{
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "garbage"),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, _ := PurgeQueueV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"shared-sqs/app/models"
|
||||
)
|
||||
|
||||
// TODO - Support:
|
||||
// - attr.MessageRetentionPeriod
|
||||
// - attr.Policy
|
||||
// - attr.RedriveAllowPolicy
|
||||
func setQueueAttributesV1(q *models.Queue, attr models.QueueAttributes) error {
|
||||
// FIXME - are there better places to put these bottom-limit validations?
|
||||
if attr.DelaySeconds >= 0 {
|
||||
q.DelaySeconds = attr.DelaySeconds.Int()
|
||||
}
|
||||
if attr.MaximumMessageSize >= 0 {
|
||||
q.MaximumMessageSize = attr.MaximumMessageSize.Int()
|
||||
}
|
||||
// TODO - bottom limit should be the AWS limits
|
||||
// The following 2 don't support zero values
|
||||
if attr.MessageRetentionPeriod > 0 {
|
||||
q.MessageRetentionPeriod = attr.MessageRetentionPeriod.Int()
|
||||
}
|
||||
if attr.ReceiveMessageWaitTimeSeconds > 0 {
|
||||
q.ReceiveMessageWaitTimeSeconds = attr.ReceiveMessageWaitTimeSeconds.Int()
|
||||
}
|
||||
if attr.VisibilityTimeout >= 0 {
|
||||
q.VisibilityTimeout = attr.VisibilityTimeout.Int()
|
||||
}
|
||||
if attr.RedrivePolicy != (models.RedrivePolicy{}) {
|
||||
arnArray := strings.Split(attr.RedrivePolicy.DeadLetterTargetArn, ":")
|
||||
queueName := arnArray[len(arnArray)-1]
|
||||
deadLetterQueue, ok := models.SyncQueues.Queues[queueName]
|
||||
if !ok {
|
||||
log.Error("Invalid RedrivePolicy Attribute")
|
||||
return fmt.Errorf("InvalidAttributeValue")
|
||||
}
|
||||
q.DeadLetterQueue = deadLetterQueue
|
||||
q.MaxReceiveCount = attr.RedrivePolicy.MaxReceiveCount.Int()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"shared-sqs/app/models"
|
||||
)
|
||||
|
||||
func TestSetQueueAttributesV1_success_no_redrive_policy(t *testing.T) {
|
||||
var emptyQueue *models.Queue
|
||||
q := &models.Queue{}
|
||||
attrs := models.QueueAttributes{
|
||||
DelaySeconds: 1,
|
||||
MaximumMessageSize: 2,
|
||||
MessageRetentionPeriod: 3,
|
||||
ReceiveMessageWaitTimeSeconds: 4,
|
||||
VisibilityTimeout: 5,
|
||||
}
|
||||
err := setQueueAttributesV1(q, attrs)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, q.DelaySeconds)
|
||||
assert.Equal(t, 2, q.MaximumMessageSize)
|
||||
assert.Equal(t, 3, q.MessageRetentionPeriod)
|
||||
assert.Equal(t, 4, q.ReceiveMessageWaitTimeSeconds)
|
||||
assert.Equal(t, 5, q.VisibilityTimeout)
|
||||
assert.Equal(t, emptyQueue, q.DeadLetterQueue)
|
||||
assert.Equal(t, 0, q.MaxReceiveCount)
|
||||
}
|
||||
|
||||
func TestSetQueueAttributesV1_success_no_request_attributes(t *testing.T) {
|
||||
var emptyQueue *models.Queue
|
||||
q := &models.Queue{}
|
||||
attrs := models.QueueAttributes{}
|
||||
err := setQueueAttributesV1(q, attrs)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 0, q.DelaySeconds)
|
||||
assert.Equal(t, 0, q.MaximumMessageSize)
|
||||
assert.Equal(t, 0, q.MessageRetentionPeriod)
|
||||
assert.Equal(t, 0, q.ReceiveMessageWaitTimeSeconds)
|
||||
assert.Equal(t, 0, q.VisibilityTimeout)
|
||||
assert.Equal(t, emptyQueue, q.DeadLetterQueue)
|
||||
assert.Equal(t, 0, q.MaxReceiveCount)
|
||||
}
|
||||
|
||||
func TestSetQueueAttributesV1_success_can_set_0_values_where_applicable(t *testing.T) {
|
||||
var emptyQueue *models.Queue
|
||||
q := &models.Queue{
|
||||
DelaySeconds: 1,
|
||||
MaximumMessageSize: 2,
|
||||
MessageRetentionPeriod: 3,
|
||||
ReceiveMessageWaitTimeSeconds: 4,
|
||||
VisibilityTimeout: 5,
|
||||
}
|
||||
attrs := models.QueueAttributes{}
|
||||
err := setQueueAttributesV1(q, attrs)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 0, q.DelaySeconds)
|
||||
assert.Equal(t, 0, q.MaximumMessageSize)
|
||||
assert.Equal(t, 3, q.MessageRetentionPeriod)
|
||||
assert.Equal(t, 4, q.ReceiveMessageWaitTimeSeconds)
|
||||
assert.Equal(t, 0, q.VisibilityTimeout)
|
||||
assert.Equal(t, emptyQueue, q.DeadLetterQueue)
|
||||
assert.Equal(t, 0, q.MaxReceiveCount)
|
||||
}
|
||||
|
||||
func TestSetQueueAttributesV1_success_with_redrive_policy(t *testing.T) {
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
}()
|
||||
|
||||
existingQueueName := "existing-queue"
|
||||
existingQueue := &models.Queue{Name: existingQueueName}
|
||||
models.SyncQueues.Queues[existingQueueName] = existingQueue
|
||||
|
||||
q := &models.Queue{}
|
||||
attrs := models.QueueAttributes{
|
||||
DelaySeconds: 1,
|
||||
MaximumMessageSize: 2,
|
||||
MessageRetentionPeriod: 3,
|
||||
ReceiveMessageWaitTimeSeconds: 4,
|
||||
VisibilityTimeout: 5,
|
||||
RedrivePolicy: models.RedrivePolicy{
|
||||
MaxReceiveCount: 10,
|
||||
DeadLetterTargetArn: fmt.Sprintf("arn:aws:sqs:region:account-id:%s", existingQueueName),
|
||||
},
|
||||
}
|
||||
err := setQueueAttributesV1(q, attrs)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 1, q.DelaySeconds)
|
||||
assert.Equal(t, 2, q.MaximumMessageSize)
|
||||
assert.Equal(t, 3, q.MessageRetentionPeriod)
|
||||
assert.Equal(t, 4, q.ReceiveMessageWaitTimeSeconds)
|
||||
assert.Equal(t, 5, q.VisibilityTimeout)
|
||||
assert.Equal(t, existingQueue, q.DeadLetterQueue)
|
||||
assert.Equal(t, 10, q.MaxReceiveCount)
|
||||
}
|
||||
|
||||
func TestSetQueueAttributesV1_error_redrive_policy_targets_missing_queue(t *testing.T) {
|
||||
existingQueueName := "existing-queue"
|
||||
|
||||
q := &models.Queue{}
|
||||
attrs := models.QueueAttributes{
|
||||
DelaySeconds: 1,
|
||||
MaximumMessageSize: 2,
|
||||
MessageRetentionPeriod: 3,
|
||||
ReceiveMessageWaitTimeSeconds: 4,
|
||||
VisibilityTimeout: 5,
|
||||
RedrivePolicy: models.RedrivePolicy{
|
||||
MaxReceiveCount: 10,
|
||||
DeadLetterTargetArn: fmt.Sprintf("arn:aws:sqs:region:account-id:%s", existingQueueName),
|
||||
},
|
||||
}
|
||||
err := setQueueAttributesV1(q, attrs)
|
||||
|
||||
assert.Error(t, err)
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// TODO - Admiral-Piett - could we refactor the way we hide messages? Change data structure to a queue
|
||||
// organized by "reveal time" or a map with the key being a timestamp of when it could be shown?
|
||||
// Ordered Map - https://github.com/elliotchance/orderedmap
|
||||
func ReceiveMessageV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
|
||||
requestBody := models.NewReceiveMessageRequest()
|
||||
ok := utils.REQUEST_TRANSFORMER(requestBody, req, false)
|
||||
if !ok {
|
||||
log.Error("Invalid Request - ReceiveMessageV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
|
||||
maxNumberOfMessages := requestBody.MaxNumberOfMessages
|
||||
if maxNumberOfMessages == 0 {
|
||||
maxNumberOfMessages = 1
|
||||
}
|
||||
|
||||
queueName := ""
|
||||
if requestBody.QueueUrl == "" {
|
||||
vars := mux.Vars(req)
|
||||
queueName = vars["queueName"]
|
||||
} else {
|
||||
uriSegments := strings.Split(requestBody.QueueUrl, "/")
|
||||
queueName = uriSegments[len(uriSegments)-1]
|
||||
}
|
||||
|
||||
if _, ok := models.SyncQueues.Queues[queueName]; !ok {
|
||||
return utils.CreateErrorResponseV1("QueueNotFound", true)
|
||||
}
|
||||
|
||||
var messages []*models.ResultMessage
|
||||
respStruct := models.ReceiveMessageResponse{}
|
||||
|
||||
waitTimeSeconds := requestBody.WaitTimeSeconds
|
||||
if waitTimeSeconds == 0 {
|
||||
models.SyncQueues.RLock()
|
||||
waitTimeSeconds = models.SyncQueues.Queues[queueName].ReceiveMessageWaitTimeSeconds
|
||||
models.SyncQueues.RUnlock()
|
||||
}
|
||||
|
||||
loops := waitTimeSeconds * 10
|
||||
for loops > 0 {
|
||||
models.SyncQueues.RLock()
|
||||
_, queueFound := models.SyncQueues.Queues[queueName]
|
||||
if !queueFound {
|
||||
models.SyncQueues.RUnlock()
|
||||
return utils.CreateErrorResponseV1("QueueNotFound", true)
|
||||
}
|
||||
messageFound := len(models.SyncQueues.Queues[queueName].Messages)-numberOfHiddenMessagesInQueue(*models.SyncQueues.Queues[queueName]) != 0
|
||||
models.SyncQueues.RUnlock()
|
||||
if !messageFound {
|
||||
continueTimer := time.NewTimer(100 * time.Millisecond)
|
||||
select {
|
||||
case <-req.Context().Done():
|
||||
continueTimer.Stop()
|
||||
return http.StatusOK, models.ReceiveMessageResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Result: models.ReceiveMessageResult{},
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
case <-continueTimer.C:
|
||||
continueTimer.Stop()
|
||||
}
|
||||
loops--
|
||||
} else {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
log.Debugf("Getting Message from Queue:%s", queueName)
|
||||
|
||||
models.SyncQueues.Lock() // Lock the Queues
|
||||
defer models.SyncQueues.Unlock() // Unlock the Queues
|
||||
|
||||
if len(models.SyncQueues.Queues[queueName].Messages) > 0 {
|
||||
numMsg := 0
|
||||
messages = make([]*models.ResultMessage, 0)
|
||||
for i := range models.SyncQueues.Queues[queueName].Messages {
|
||||
if numMsg >= maxNumberOfMessages {
|
||||
break
|
||||
}
|
||||
|
||||
if models.SyncQueues.Queues[queueName].Messages[i].ReceiptHandle != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
msg := &models.SyncQueues.Queues[queueName].Messages[i]
|
||||
if !msg.IsReadyForReceipt() {
|
||||
continue
|
||||
}
|
||||
|
||||
if models.SyncQueues.Queues[queueName].IsFIFO {
|
||||
// If we got messages here it means we have not processed it yet, so get next
|
||||
if models.SyncQueues.Queues[queueName].IsLocked(msg.GroupID) {
|
||||
continue
|
||||
}
|
||||
// Otherwise lock messages for group ID
|
||||
models.SyncQueues.Queues[queueName].LockGroup(msg.GroupID)
|
||||
}
|
||||
|
||||
randomId := uuid.NewString()
|
||||
msg.ReceiptHandle = msg.Uuid + "#" + randomId
|
||||
msg.ReceiptTime = time.Now().UTC()
|
||||
|
||||
if requestBody.VisibilityTimeout != 0 {
|
||||
msg.VisibilityTimeout = time.Now().Add(time.Duration(requestBody.VisibilityTimeout) * time.Second)
|
||||
} else {
|
||||
msg.VisibilityTimeout = time.Now().Add(time.Duration(models.SyncQueues.Queues[queueName].VisibilityTimeout) * time.Second)
|
||||
}
|
||||
|
||||
messages = append(messages, buildResultMessage(msg))
|
||||
|
||||
numMsg++
|
||||
}
|
||||
|
||||
respStruct = models.ReceiveMessageResponse{
|
||||
"http://queue.amazonaws.com/doc/2012-11-05/",
|
||||
models.ReceiveMessageResult{
|
||||
Messages: messages,
|
||||
},
|
||||
models.ResponseMetadata{
|
||||
RequestId: "00000000-0000-0000-0000-000000000000",
|
||||
},
|
||||
}
|
||||
} else {
|
||||
log.Warning("No messages in Queue:", queueName)
|
||||
respStruct = models.ReceiveMessageResponse{Xmlns: "http://queue.amazonaws.com/doc/2012-11-05/", Result: models.ReceiveMessageResult{}, Metadata: models.ResponseMetadata{RequestId: "00000000-0000-0000-0000-000000000000"}}
|
||||
}
|
||||
|
||||
return http.StatusOK, respStruct
|
||||
}
|
||||
|
||||
func buildResultMessage(m *models.SqsMessage) *models.ResultMessage {
|
||||
return &models.ResultMessage{
|
||||
MessageId: m.Uuid,
|
||||
Body: m.MessageBody,
|
||||
ReceiptHandle: m.ReceiptHandle,
|
||||
MD5OfBody: utils.GetMD5Hash(m.MessageBody),
|
||||
MD5OfMessageAttributes: m.MD5OfMessageAttributes,
|
||||
MessageAttributes: m.MessageAttributes,
|
||||
Attributes: map[string]string{
|
||||
"ApproximateFirstReceiveTimestamp": fmt.Sprintf("%d", m.ReceiptTime.UnixNano()/int64(time.Millisecond)),
|
||||
"SenderId": models.CurrentEnvironment.AccountID,
|
||||
"ApproximateReceiveCount": fmt.Sprintf("%d", m.NumberOfReceives+1),
|
||||
"SentTimestamp": fmt.Sprintf("%d", time.Now().UTC().UnixNano()/int64(time.Millisecond)),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"shared-sqs/app/test"
|
||||
|
||||
"shared-sqs/app/fixtures"
|
||||
"shared-sqs/app/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// TODO Admiral-Piett - fix all these tests, we don't want to be waiting for 5 seconds plus like this.
|
||||
func TestReceiveMessageV1_with_WaitTimeEnforced(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
}()
|
||||
|
||||
q := &models.Queue{
|
||||
Name: "waiting-queue",
|
||||
ReceiveMessageWaitTimeSeconds: 2,
|
||||
//MaximumMessageSize: 262144,
|
||||
}
|
||||
models.SyncQueues.Queues["waiting-queue"] = q
|
||||
|
||||
// receive message ensure delay
|
||||
_, r := test.GenerateRequestInfo("POST", "/", models.ReceiveMessageRequest{
|
||||
QueueUrl: "http://localhost:4100/queue/waiting-queue",
|
||||
}, true)
|
||||
|
||||
start := time.Now()
|
||||
status, response := ReceiveMessageV1(r)
|
||||
elapsed := time.Since(start)
|
||||
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
if elapsed < 2*time.Second {
|
||||
t.Fatalf("handler didn't wait ReceiveMessageWaitTimeSeconds %s", response)
|
||||
}
|
||||
|
||||
// mock sending a message
|
||||
q.Messages = append(q.Messages, models.SqsMessage{MessageBody: "1"})
|
||||
|
||||
// receive message
|
||||
_, r = test.GenerateRequestInfo("POST", "/", models.ReceiveMessageRequest{
|
||||
QueueUrl: "http://localhost:4100/queue/waiting-queue",
|
||||
}, true)
|
||||
start = time.Now()
|
||||
status, resp := ReceiveMessageV1(r)
|
||||
elapsed = time.Since(start)
|
||||
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
if elapsed > 1*time.Second {
|
||||
t.Fatal("handler waited when message was available, expected not to wait")
|
||||
}
|
||||
|
||||
assert.Equal(t, "1", string(resp.GetResult().(models.ReceiveMessageResult).Messages[0].Body))
|
||||
}
|
||||
|
||||
func TestReceiveMessageV1_CanceledByClient(t *testing.T) {
|
||||
// create a queue
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
}()
|
||||
|
||||
q := &models.Queue{
|
||||
Name: "cancel-queue",
|
||||
ReceiveMessageWaitTimeSeconds: 20,
|
||||
}
|
||||
models.SyncQueues.Queues["cancel-queue"] = q
|
||||
|
||||
var wg sync.WaitGroup
|
||||
ctx, cancelReceive := context.WithCancel(context.Background())
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// receive message (that will be canceled)
|
||||
_, r := test.GenerateRequestInfo("POST", "/", models.ReceiveMessageRequest{
|
||||
QueueUrl: "http://localhost:4100/queue/cancel-queue",
|
||||
}, true)
|
||||
r = r.WithContext(ctx)
|
||||
|
||||
status, resp := ReceiveMessageV1(r)
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
|
||||
if len(resp.GetResult().(models.ReceiveMessageResult).Messages) != 0 {
|
||||
t.Fatal("expecting this ReceiveMessage() to not pickup this message as it should canceled before the Send()")
|
||||
}
|
||||
}()
|
||||
time.Sleep(100 * time.Millisecond) // let enought time for the Receive go to wait mode
|
||||
cancelReceive() // cancel the first ReceiveMessage(), make sure it will not pickup the sent message below
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
|
||||
// send a message
|
||||
_, r := test.GenerateRequestInfo("POST", "/", models.SendMessageRequest{
|
||||
QueueUrl: "http://localhost:4100/queue/cancel-queue",
|
||||
MessageBody: "12345",
|
||||
}, true)
|
||||
status, _ := SendMessageV1(r)
|
||||
if status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got \n%v want %v", status, http.StatusOK)
|
||||
}
|
||||
|
||||
// receive message
|
||||
_, r = test.GenerateRequestInfo("POST", "/", models.ReceiveMessageRequest{
|
||||
QueueUrl: "http://localhost:4100/queue/cancel-queue",
|
||||
}, true)
|
||||
start := time.Now()
|
||||
status, resp := ReceiveMessageV1(r)
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
elapsed := time.Since(start)
|
||||
|
||||
result, ok := resp.GetResult().(models.ReceiveMessageResult)
|
||||
if !ok {
|
||||
t.Fatal("handler should return a message")
|
||||
}
|
||||
|
||||
if len(result.Messages) == 0 || string(result.Messages[0].Body) == "12345\n" {
|
||||
t.Fatal("handler should return a message")
|
||||
}
|
||||
if elapsed > 1*time.Second {
|
||||
t.Fatal("handler waited when message was available, expected not to wait")
|
||||
}
|
||||
|
||||
if timedout := waitTimeout(&wg, 2*time.Second); timedout {
|
||||
t.Errorf("expected ReceiveMessage() in goroutine to exit quickly due to cancelReceive() called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReceiveMessageV1_with_DelaySeconds(t *testing.T) {
|
||||
// create a queue
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
}()
|
||||
|
||||
q := &models.Queue{
|
||||
Name: "delay-seconds-queue",
|
||||
DelaySeconds: 2,
|
||||
}
|
||||
models.SyncQueues.Queues["delay-seconds-queue"] = q
|
||||
|
||||
// send a message
|
||||
_, r := test.GenerateRequestInfo("POST", "/", models.SendMessageRequest{
|
||||
QueueUrl: "http://localhost:4100/queue/delay-seconds-queue",
|
||||
MessageBody: "1",
|
||||
}, true)
|
||||
status, _ := SendMessageV1(r)
|
||||
if status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got \n%v want %v", status, http.StatusOK)
|
||||
}
|
||||
|
||||
// receive message before delay is up
|
||||
_, r = test.GenerateRequestInfo("POST", "/", models.ReceiveMessageRequest{QueueUrl: "http://localhost:4100/queue/delay-seconds-queue"}, true)
|
||||
status, _ = ReceiveMessageV1(r)
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
|
||||
// receive message with wait should return after delay
|
||||
_, r = test.GenerateRequestInfo("POST", "/", models.ReceiveMessageRequest{
|
||||
QueueUrl: "http://localhost:4100/queue/delay-seconds-queue",
|
||||
WaitTimeSeconds: 10,
|
||||
}, true)
|
||||
start := time.Now()
|
||||
status, _ = ReceiveMessageV1(r)
|
||||
elapsed := time.Since(start)
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
if elapsed < 1*time.Second {
|
||||
t.Errorf("handler didn't wait at all")
|
||||
}
|
||||
if elapsed > 4*time.Second {
|
||||
t.Errorf("handler didn't need to wait all WaitTimeSeconds=10, only DelaySeconds=2")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReceiveMessageV1_with_MessageAttributes(t *testing.T) {
|
||||
// create a queue
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
}()
|
||||
|
||||
q := &models.Queue{Name: "waiting-queue"}
|
||||
models.SyncQueues.Queues["waiting-queue"] = q
|
||||
|
||||
// send a message
|
||||
q.Messages = append(q.Messages, models.SqsMessage{
|
||||
MessageBody: "1",
|
||||
MessageAttributes: map[string]models.MessageAttribute{
|
||||
"TestMessageAttrName": {
|
||||
DataType: "String",
|
||||
StringValue: "TestMessageAttrValue",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// receive message
|
||||
_, r := test.GenerateRequestInfo("POST", "/", models.ReceiveMessageRequest{QueueUrl: "http://localhost:4100/queue/waiting-queue"}, true)
|
||||
status, resp := ReceiveMessageV1(r)
|
||||
result := resp.GetResult().(models.ReceiveMessageResult)
|
||||
|
||||
assert.NotEmpty(t, result.Messages[0].Attributes["ApproximateFirstReceiveTimestamp"])
|
||||
assert.NotEmpty(t, result.Messages[0].Attributes["SenderId"])
|
||||
assert.NotEmpty(t, result.Messages[0].Attributes["ApproximateReceiveCount"])
|
||||
assert.NotEmpty(t, result.Messages[0].Attributes["SentTimestamp"])
|
||||
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
assert.Equal(t, "1", string(result.Messages[0].Body))
|
||||
assert.Equal(t, 1, len(result.Messages[0].MessageAttributes))
|
||||
assert.Equal(t, "String", result.Messages[0].MessageAttributes["TestMessageAttrName"].DataType)
|
||||
assert.Equal(t, "TestMessageAttrValue", result.Messages[0].MessageAttributes["TestMessageAttrName"].StringValue)
|
||||
}
|
||||
|
||||
func TestReceiveMessageV1_request_transformer_error(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func TestReceiveMessageV1_with_CustomVisibilityTimeout(t *testing.T) {
|
||||
// create a queue
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
}()
|
||||
|
||||
// Create a queue with a default visibility timeout of 30 seconds
|
||||
q := &models.Queue{
|
||||
Name: "custom-visibility-queue",
|
||||
VisibilityTimeout: 30,
|
||||
}
|
||||
models.SyncQueues.Queues["custom-visibility-queue"] = q
|
||||
|
||||
// Add a message to the queue
|
||||
q.Messages = append(q.Messages, models.SqsMessage{
|
||||
MessageBody: "test-message",
|
||||
Uuid: "test-uuid",
|
||||
})
|
||||
|
||||
// Test 1: Receive message with custom visibility timeout
|
||||
customTimeout := 60 // 60 seconds
|
||||
_, r := test.GenerateRequestInfo("POST", "/", models.ReceiveMessageRequest{
|
||||
QueueUrl: "http://localhost:4100/queue/custom-visibility-queue",
|
||||
VisibilityTimeout: customTimeout,
|
||||
}, true)
|
||||
|
||||
status, resp := ReceiveMessageV1(r)
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
|
||||
result := resp.GetResult().(models.ReceiveMessageResult)
|
||||
assert.Equal(t, 1, len(result.Messages))
|
||||
assert.Equal(t, "test-message", string(result.Messages[0].Body))
|
||||
|
||||
// Verify the message in the queue has the custom visibility timeout
|
||||
// We can't directly check the exact time, but we can verify it's not using the queue's default
|
||||
// by checking that the visibility timeout is greater than now + default timeout - 1 second
|
||||
// and less than now + custom timeout + 1 second
|
||||
now := time.Now()
|
||||
defaultExpiry := now.Add(time.Duration(q.VisibilityTimeout) * time.Second)
|
||||
customExpiry := now.Add(time.Duration(customTimeout) * time.Second)
|
||||
|
||||
// The first message should have the custom visibility timeout
|
||||
msgVisibilityTimeout := q.Messages[0].VisibilityTimeout
|
||||
assert.True(t, msgVisibilityTimeout.After(defaultExpiry.Add(-1*time.Second)),
|
||||
"Message visibility timeout should be greater than default timeout")
|
||||
assert.True(t, msgVisibilityTimeout.Before(customExpiry.Add(1*time.Second)),
|
||||
"Message visibility timeout should be less than custom timeout + 1 second")
|
||||
|
||||
// Test 2: Reset the queue and test with zero visibility timeout (should use queue default)
|
||||
models.SyncQueues.Queues["custom-visibility-queue"] = &models.Queue{
|
||||
Name: "custom-visibility-queue",
|
||||
VisibilityTimeout: 30,
|
||||
}
|
||||
q = models.SyncQueues.Queues["custom-visibility-queue"]
|
||||
q.Messages = append(q.Messages, models.SqsMessage{
|
||||
MessageBody: "test-message-2",
|
||||
Uuid: "test-uuid-2",
|
||||
})
|
||||
|
||||
// Receive message with zero visibility timeout (should use queue default)
|
||||
_, r = test.GenerateRequestInfo("POST", "/", models.ReceiveMessageRequest{
|
||||
QueueUrl: "http://localhost:4100/queue/custom-visibility-queue",
|
||||
VisibilityTimeout: 0, // Zero should use queue default
|
||||
}, true)
|
||||
|
||||
status, resp = ReceiveMessageV1(r)
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
|
||||
// Verify the message in the queue has the default visibility timeout
|
||||
now = time.Now()
|
||||
defaultExpiry = now.Add(time.Duration(q.VisibilityTimeout) * time.Second)
|
||||
|
||||
// The message should have the default visibility timeout
|
||||
msgVisibilityTimeout = q.Messages[0].VisibilityTimeout
|
||||
assert.True(t, msgVisibilityTimeout.After(defaultExpiry.Add(-1*time.Second)),
|
||||
"Message visibility timeout should be greater than default timeout - 1 second")
|
||||
assert.True(t, msgVisibilityTimeout.Before(defaultExpiry.Add(1*time.Second)),
|
||||
"Message visibility timeout should be less than default timeout + 1 second")
|
||||
}
|
||||
|
||||
func TestReceiveMessageV1_FIFOSecondMessageAvailableAfterDelete(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
}()
|
||||
|
||||
queueName := "fifo-delay-queue"
|
||||
queueURL := fmt.Sprintf("http://localhost:4100/queue/%s", queueName)
|
||||
now := time.Now().Add(-1 * time.Minute)
|
||||
|
||||
// create a queue with a visibility timeout of 10 seconds
|
||||
q := &models.Queue{
|
||||
Name: queueName,
|
||||
VisibilityTimeout: 10,
|
||||
IsFIFO: true,
|
||||
FIFOMessages: map[string]int{},
|
||||
FIFOSequenceNumbers: map[string]int{},
|
||||
Duplicates: map[string]time.Time{},
|
||||
Messages: []models.SqsMessage{
|
||||
{
|
||||
MessageBody: "first",
|
||||
Uuid: "first-uuid",
|
||||
GroupID: "company#worker",
|
||||
SentTime: now,
|
||||
},
|
||||
{
|
||||
MessageBody: "second",
|
||||
Uuid: "second-uuid",
|
||||
GroupID: "company#worker",
|
||||
SentTime: now,
|
||||
},
|
||||
},
|
||||
}
|
||||
models.SyncQueues.Queues[queueName] = q
|
||||
|
||||
// receive the first FIFO message only
|
||||
_, r := test.GenerateRequestInfo("POST", "/", models.ReceiveMessageRequest{
|
||||
QueueUrl: queueURL,
|
||||
MaxNumberOfMessages: 1,
|
||||
}, true)
|
||||
status, resp := ReceiveMessageV1(r)
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
result := resp.GetResult().(models.ReceiveMessageResult)
|
||||
if len(result.Messages) != 1 {
|
||||
t.Fatalf("expected to receive the first FIFO message, got %d", len(result.Messages))
|
||||
}
|
||||
assert.Equal(t, "first", result.Messages[0].Body)
|
||||
|
||||
firstReceipt := result.Messages[0].ReceiptHandle
|
||||
|
||||
// verify the second FIFO message is blocked while the first is in flight
|
||||
_, r = test.GenerateRequestInfo("POST", "/", models.ReceiveMessageRequest{
|
||||
QueueUrl: queueURL,
|
||||
}, true)
|
||||
status, resp = ReceiveMessageV1(r)
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
result = resp.GetResult().(models.ReceiveMessageResult)
|
||||
if len(result.Messages) != 0 {
|
||||
t.Fatalf("expected no FIFO message while the first is outstanding, got %d", len(result.Messages))
|
||||
}
|
||||
|
||||
// delete the first FIFO message
|
||||
_, deleteReq := test.GenerateRequestInfo("POST", "/", models.DeleteMessageRequest{
|
||||
QueueUrl: queueURL,
|
||||
ReceiptHandle: firstReceipt,
|
||||
}, true)
|
||||
deleteStatus, _ := DeleteMessageV1(deleteReq)
|
||||
assert.Equal(t, http.StatusOK, deleteStatus)
|
||||
|
||||
// receive the second FIFO message and ensure it does not wait for full 10 second visibility timeout
|
||||
_, r = test.GenerateRequestInfo("POST", "/", models.ReceiveMessageRequest{
|
||||
QueueUrl: queueURL,
|
||||
}, true)
|
||||
start := time.Now()
|
||||
status, resp = ReceiveMessageV1(r)
|
||||
elapsed := time.Since(start)
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
result = resp.GetResult().(models.ReceiveMessageResult)
|
||||
if len(result.Messages) != 1 {
|
||||
t.Fatalf("expected second FIFO message to be available immediately, got %d", len(result.Messages))
|
||||
}
|
||||
if elapsed > time.Second {
|
||||
t.Fatalf("expected second FIFO message without waiting on visibility timeout, took %s", elapsed)
|
||||
}
|
||||
assert.Equal(t, "second", result.Messages[0].Body)
|
||||
|
||||
// delete the second FIFO message
|
||||
_, deleteReq = test.GenerateRequestInfo("POST", "/", models.DeleteMessageRequest{
|
||||
QueueUrl: queueURL,
|
||||
ReceiptHandle: result.Messages[0].ReceiptHandle,
|
||||
}, true)
|
||||
deleteStatus, _ = DeleteMessageV1(deleteReq)
|
||||
assert.Equal(t, http.StatusOK, deleteStatus)
|
||||
|
||||
if len(q.Messages) != 0 {
|
||||
t.Fatalf("expected all FIFO messages to be deleted, remaining %d", len(q.Messages))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO - other tests
|
||||
@@ -0,0 +1,100 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
|
||||
"shared-sqs/app/utils"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func SendMessageV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
|
||||
requestBody := models.NewSendMessageRequest()
|
||||
ok := utils.REQUEST_TRANSFORMER(requestBody, req, false)
|
||||
if !ok {
|
||||
log.Error("Invalid Request - SendMessageV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
messageBody := requestBody.MessageBody
|
||||
messageGroupID := requestBody.MessageGroupId
|
||||
messageDeduplicationID := requestBody.MessageDeduplicationId
|
||||
|
||||
queueUrl := getQueueFromPath(requestBody.QueueUrl, req.URL.String())
|
||||
|
||||
queueName := ""
|
||||
if queueUrl == "" {
|
||||
// TODO: Remove this query param logic if it's not still valid or something
|
||||
vars := mux.Vars(req)
|
||||
queueName = vars["queueName"]
|
||||
} else {
|
||||
uriSegments := strings.Split(queueUrl, "/")
|
||||
queueName = uriSegments[len(uriSegments)-1]
|
||||
}
|
||||
|
||||
if _, ok := models.SyncQueues.Queues[queueName]; !ok {
|
||||
// Queue does not exist
|
||||
return utils.CreateErrorResponseV1("QueueNotFound", true)
|
||||
}
|
||||
|
||||
if models.SyncQueues.Queues[queueName].MaximumMessageSize > 0 &&
|
||||
len(messageBody) > models.SyncQueues.Queues[queueName].MaximumMessageSize {
|
||||
// Message size is too big
|
||||
return utils.CreateErrorResponseV1("MessageTooBig", true)
|
||||
}
|
||||
|
||||
delaySecs := models.SyncQueues.Queues[queueName].DelaySeconds
|
||||
if requestBody.DelaySeconds != 0 {
|
||||
delaySecs = requestBody.DelaySeconds
|
||||
}
|
||||
|
||||
log.Debugf("Putting Message in Queue: [%s]", queueName)
|
||||
msg := models.SqsMessage{MessageBody: messageBody}
|
||||
if len(requestBody.MessageAttributes) > 0 {
|
||||
msg.MessageAttributes = requestBody.MessageAttributes
|
||||
msg.MD5OfMessageAttributes = utils.HashAttributes(requestBody.MessageAttributes)
|
||||
}
|
||||
msg.MD5OfMessageBody = utils.GetMD5Hash(messageBody)
|
||||
msg.Uuid = uuid.NewString()
|
||||
msg.GroupID = messageGroupID
|
||||
msg.DeduplicationID = messageDeduplicationID
|
||||
msg.SentTime = time.Now()
|
||||
msg.DelaySecs = delaySecs
|
||||
|
||||
models.SyncQueues.Lock()
|
||||
fifoSeqNumber := ""
|
||||
if models.SyncQueues.Queues[queueName].IsFIFO {
|
||||
fifoSeqNumber = models.SyncQueues.Queues[queueName].NextSequenceNumber(messageGroupID)
|
||||
}
|
||||
|
||||
if !models.SyncQueues.Queues[queueName].IsDuplicate(messageDeduplicationID) {
|
||||
models.SyncQueues.Queues[queueName].Messages = append(models.SyncQueues.Queues[queueName].Messages, msg)
|
||||
} else {
|
||||
log.Debugf("Message with deduplicationId [%s] in queue [%s] is duplicate ", messageDeduplicationID, queueName)
|
||||
}
|
||||
|
||||
models.SyncQueues.Queues[queueName].InitDuplicatation(messageDeduplicationID)
|
||||
models.SyncQueues.Unlock()
|
||||
log.Infof("%s: Queue: %s, Message: %s\n", time.Now().Format("2006-01-02 15:04:05"), queueName, msg.MessageBody)
|
||||
|
||||
respStruct := models.SendMessageResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Result: models.SendMessageResult{
|
||||
MD5OfMessageAttributes: msg.MD5OfMessageAttributes,
|
||||
MD5OfMessageBody: msg.MD5OfMessageBody,
|
||||
MessageId: msg.Uuid,
|
||||
SequenceNumber: fifoSeqNumber,
|
||||
},
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
|
||||
return http.StatusOK, respStruct
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func SendMessageBatchV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
|
||||
requestBody := models.NewSendMessageBatchRequest()
|
||||
ok := utils.REQUEST_TRANSFORMER(requestBody, req, false)
|
||||
if !ok {
|
||||
log.Error("Invalid Request - SendMessageBatchV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
|
||||
queueUrl := requestBody.QueueUrl
|
||||
|
||||
// TODO: Remove this query param logic if it's not still valid or something
|
||||
queueName := ""
|
||||
if queueUrl == "" {
|
||||
vars := mux.Vars(req)
|
||||
queueName = vars["queueName"]
|
||||
} else {
|
||||
uriSegments := strings.Split(queueUrl, "/")
|
||||
queueName = uriSegments[len(uriSegments)-1]
|
||||
}
|
||||
|
||||
if _, ok := models.SyncQueues.Queues[queueName]; !ok {
|
||||
return utils.CreateErrorResponseV1("QueueNotFound", true)
|
||||
}
|
||||
|
||||
sendEntries := requestBody.Entries
|
||||
|
||||
if len(sendEntries) == 0 {
|
||||
return utils.CreateErrorResponseV1("EmptyBatchRequest", true)
|
||||
}
|
||||
|
||||
if len(sendEntries) > 10 {
|
||||
return utils.CreateErrorResponseV1("TooManyEntriesInBatchRequest", true)
|
||||
}
|
||||
ids := map[string]struct{}{}
|
||||
for _, v := range sendEntries {
|
||||
if _, ok := ids[v.Id]; ok {
|
||||
return utils.CreateErrorResponseV1("BatchEntryIdsNotDistinct", true)
|
||||
}
|
||||
ids[v.Id] = struct{}{}
|
||||
}
|
||||
|
||||
sentEntries := make([]models.SendMessageBatchResultEntry, 0)
|
||||
log.Debug("Putting Message in Queue:", queueName)
|
||||
for _, sendEntry := range sendEntries {
|
||||
msg := models.SqsMessage{MessageBody: sendEntry.MessageBody}
|
||||
if len(sendEntry.MessageAttributes) > 0 {
|
||||
msg.MessageAttributes = sendEntry.MessageAttributes
|
||||
msg.MD5OfMessageAttributes = utils.HashAttributes(sendEntry.MessageAttributes)
|
||||
}
|
||||
msg.MD5OfMessageBody = utils.GetMD5Hash(sendEntry.MessageBody)
|
||||
msg.GroupID = sendEntry.MessageGroupId
|
||||
msg.DeduplicationID = sendEntry.MessageDeduplicationId
|
||||
msg.Uuid = uuid.NewString()
|
||||
msg.SentTime = time.Now()
|
||||
models.SyncQueues.Lock()
|
||||
fifoSeqNumber := ""
|
||||
if models.SyncQueues.Queues[queueName].IsFIFO {
|
||||
fifoSeqNumber = models.SyncQueues.Queues[queueName].NextSequenceNumber(sendEntry.MessageGroupId)
|
||||
}
|
||||
|
||||
if !models.SyncQueues.Queues[queueName].IsDuplicate(sendEntry.MessageDeduplicationId) {
|
||||
models.SyncQueues.Queues[queueName].Messages = append(models.SyncQueues.Queues[queueName].Messages, msg)
|
||||
} else {
|
||||
log.Debugf("Message with deduplicationId [%s] in queue [%s] is duplicate ", sendEntry.MessageDeduplicationId, queueName)
|
||||
}
|
||||
|
||||
models.SyncQueues.Queues[queueName].InitDuplicatation(sendEntry.MessageDeduplicationId)
|
||||
|
||||
models.SyncQueues.Unlock()
|
||||
se := models.SendMessageBatchResultEntry{
|
||||
Id: sendEntry.Id,
|
||||
MessageId: msg.Uuid,
|
||||
MD5OfMessageBody: msg.MD5OfMessageBody,
|
||||
MD5OfMessageAttributes: msg.MD5OfMessageAttributes,
|
||||
SequenceNumber: fifoSeqNumber,
|
||||
}
|
||||
sentEntries = append(sentEntries, se)
|
||||
log.Infof("%s: Queue: %s, Message: %s\n", time.Now().Format("2006-01-02 15:04:05"), queueName, msg.MessageBody)
|
||||
}
|
||||
|
||||
respStruct := models.SendMessageBatchResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Result: models.SendMessageBatchResult{Entry: sentEntries},
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
|
||||
return http.StatusOK, respStruct
|
||||
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"shared-sqs/app/conf"
|
||||
"shared-sqs/app/fixtures"
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/test"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSendMessageBatchV1_Success(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
sendMessageRequest_success := models.SendMessageBatchRequest{
|
||||
Entries: []models.SendMessageBatchRequestEntry{
|
||||
{
|
||||
Id: "test-msg-with-non-attribute",
|
||||
MessageBody: "test%20message%20body%201",
|
||||
},
|
||||
{
|
||||
Id: "test-msg-with-single-attirbute",
|
||||
MessageBody: "test%20message%20body%202",
|
||||
MessageAttributes: map[string]models.MessageAttribute{
|
||||
"my-attribute-name": {
|
||||
DataType: "String",
|
||||
StringValue: "my-attribute-string-value",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: "test-msg-with-multi-attirbute",
|
||||
MessageBody: "test%20message%20body%203",
|
||||
MessageAttributes: map[string]models.MessageAttribute{
|
||||
"my-attribute-name-1": {
|
||||
BinaryValue: "binary-value-1",
|
||||
DataType: "Binary",
|
||||
},
|
||||
"my-attribute-name-2": {
|
||||
DataType: "String",
|
||||
StringValue: "my-attribute-string-value-2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "unit-queue1"),
|
||||
}
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.SendMessageBatchRequest)
|
||||
*v = sendMessageRequest_success
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
status, response := SendMessageBatchV1(r)
|
||||
sendMessageBatchResponse, ok := response.(models.SendMessageBatchResponse)
|
||||
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
assert.True(t, ok)
|
||||
|
||||
resultEntry := sendMessageBatchResponse.Result.Entry
|
||||
assert.Equal(t, 3, len(resultEntry))
|
||||
assert.Contains(t, resultEntry[0].Id, "test-msg-with-non-attribute")
|
||||
assert.Contains(t, resultEntry[1].Id, "test-msg-with-single-attirbute")
|
||||
assert.Contains(t, resultEntry[2].Id, "test-msg-with-multi-attirbute")
|
||||
assert.Empty(t, resultEntry[0].SequenceNumber)
|
||||
assert.Empty(t, resultEntry[1].SequenceNumber)
|
||||
assert.Empty(t, resultEntry[2].SequenceNumber)
|
||||
|
||||
}
|
||||
|
||||
func TestSendMessageBatchV1_Success_Fifo_Queue(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
sendMessageRequest_success := models.SendMessageBatchRequest{
|
||||
Entries: []models.SendMessageBatchRequestEntry{
|
||||
{
|
||||
Id: "test_msg_001",
|
||||
MessageBody: "test%20message%20body%201",
|
||||
},
|
||||
{
|
||||
Id: "test_msg_002",
|
||||
MessageBody: "test%20message%20body%202",
|
||||
},
|
||||
{
|
||||
Id: "test_msg_003",
|
||||
MessageBody: "test%20message%20body%203",
|
||||
},
|
||||
},
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "fifo-queue-1"),
|
||||
}
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.SendMessageBatchRequest)
|
||||
*v = sendMessageRequest_success
|
||||
return true
|
||||
}
|
||||
|
||||
q := &models.Queue{
|
||||
Name: "fifo-queue-1",
|
||||
MaximumMessageSize: 1024,
|
||||
IsFIFO: true,
|
||||
}
|
||||
models.SyncQueues.Queues["fifo-queue-1"] = q
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
status, response := SendMessageBatchV1(r)
|
||||
sendMessageBatchResponse, ok := response.(models.SendMessageBatchResponse)
|
||||
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
assert.True(t, ok)
|
||||
|
||||
resultEntry := sendMessageBatchResponse.Result.Entry
|
||||
assert.Equal(t, 3, len(resultEntry))
|
||||
assert.Contains(t, resultEntry[0].Id, "test_msg_001")
|
||||
assert.NotEmpty(t, resultEntry[0].SequenceNumber)
|
||||
assert.Contains(t, resultEntry[1].Id, "test_msg_002")
|
||||
assert.NotEmpty(t, resultEntry[1].SequenceNumber)
|
||||
assert.Contains(t, resultEntry[2].Id, "test_msg_003")
|
||||
assert.NotEmpty(t, resultEntry[2].SequenceNumber)
|
||||
}
|
||||
|
||||
func TestSendMessageBatchV1_Error_QueueNotFound(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
sendMessageRequest_not_found_queue := models.SendMessageBatchRequest{
|
||||
Entries: []models.SendMessageBatchRequestEntry{
|
||||
{
|
||||
Id: "test_msg_001",
|
||||
MessageBody: "test%20message%20body%201",
|
||||
},
|
||||
{
|
||||
Id: "test_msg_002",
|
||||
MessageBody: "test%20message%20body%202",
|
||||
},
|
||||
{
|
||||
Id: "test_msg_003",
|
||||
MessageBody: "test%20message%20body%203",
|
||||
},
|
||||
},
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "not-exist-queue1"),
|
||||
}
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.SendMessageBatchRequest)
|
||||
*v = sendMessageRequest_not_found_queue
|
||||
return true
|
||||
}
|
||||
|
||||
expected := models.ErrorResult{
|
||||
Type: "Not Found",
|
||||
Code: "AWS.SimpleQueueService.NonExistentQueue",
|
||||
Message: "The specified queue does not exist for this wsdl version.",
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
status, response := SendMessageBatchV1(r)
|
||||
errorResult := response.GetResult().(models.ErrorResult)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, status)
|
||||
assert.Equal(t, expected, errorResult)
|
||||
}
|
||||
|
||||
func TestSendMessageBatchV1_Error_NoEntry(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
sendMessageRequest_no_entry := models.SendMessageBatchRequest{
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "unit-queue1"),
|
||||
}
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.SendMessageBatchRequest)
|
||||
*v = sendMessageRequest_no_entry
|
||||
return true
|
||||
}
|
||||
|
||||
expected := models.ErrorResult{
|
||||
Type: "EmptyBatchRequest",
|
||||
Code: "AWS.SimpleQueueService.EmptyBatchRequest",
|
||||
Message: "The batch request doesn't contain any entries.",
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
status, response := SendMessageBatchV1(r)
|
||||
errorResult := response.GetResult().(models.ErrorResult)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, status)
|
||||
assert.Equal(t, expected, errorResult)
|
||||
}
|
||||
|
||||
func TestSendMessageBatchV1_Error_IdNotDistinct(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
sendMessageRequest_no_entry := models.SendMessageBatchRequest{
|
||||
Entries: []models.SendMessageBatchRequestEntry{
|
||||
{
|
||||
Id: "test_msg_001",
|
||||
MessageBody: "test%20message%20body%201",
|
||||
},
|
||||
{
|
||||
Id: "test_msg_001",
|
||||
MessageBody: "test%20message%20body%202",
|
||||
},
|
||||
{
|
||||
Id: "test_msg_001",
|
||||
MessageBody: "test%20message%20body%203",
|
||||
},
|
||||
},
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "unit-queue1"),
|
||||
}
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.SendMessageBatchRequest)
|
||||
*v = sendMessageRequest_no_entry
|
||||
return true
|
||||
}
|
||||
|
||||
expected := models.ErrorResult{
|
||||
Type: "BatchEntryIdsNotDistinct",
|
||||
Code: "AWS.SimpleQueueService.BatchEntryIdsNotDistinct",
|
||||
Message: "Two or more batch entries in the request have the same Id.",
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
status, response := SendMessageBatchV1(r)
|
||||
errorResult := response.GetResult().(models.ErrorResult)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, status)
|
||||
assert.Equal(t, expected, errorResult)
|
||||
}
|
||||
|
||||
func TestSendMessageBatchV1_Error_TooManyEntries(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
sendMessageRequest_success := models.SendMessageBatchRequest{
|
||||
Entries: []models.SendMessageBatchRequestEntry{
|
||||
{
|
||||
Id: "test_msg_001",
|
||||
MessageBody: "test%20message%20body%201",
|
||||
},
|
||||
{
|
||||
Id: "test_msg_002",
|
||||
MessageBody: "test%20message%20body%202",
|
||||
},
|
||||
{
|
||||
Id: "test_msg_003",
|
||||
MessageBody: "test%20message%20body%203",
|
||||
},
|
||||
{
|
||||
Id: "test_msg_004",
|
||||
MessageBody: "test%20message%20body%204",
|
||||
},
|
||||
{
|
||||
Id: "test_msg_005",
|
||||
MessageBody: "test%20message%20body%205",
|
||||
},
|
||||
{
|
||||
Id: "test_msg_006",
|
||||
MessageBody: "test%20message%20body%206",
|
||||
},
|
||||
{
|
||||
Id: "test_msg_007",
|
||||
MessageBody: "test%20message%20body%207",
|
||||
},
|
||||
{
|
||||
Id: "test_msg_008",
|
||||
MessageBody: "test%20message%20body%208",
|
||||
},
|
||||
{
|
||||
Id: "test_msg_009",
|
||||
MessageBody: "test%20message%20body%209",
|
||||
},
|
||||
{
|
||||
Id: "test_msg_010",
|
||||
MessageBody: "test%20message%20body%210",
|
||||
},
|
||||
{
|
||||
Id: "test_msg_011",
|
||||
MessageBody: "test%20message%20body%211",
|
||||
},
|
||||
},
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "unit-queue1"),
|
||||
}
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.SendMessageBatchRequest)
|
||||
*v = sendMessageRequest_success
|
||||
return true
|
||||
}
|
||||
|
||||
expected := models.ErrorResult{
|
||||
Type: "TooManyEntriesInBatchRequest",
|
||||
Code: "AWS.SimpleQueueService.TooManyEntriesInBatchRequest",
|
||||
Message: "Maximum number of entries per request are 10.",
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
status, response := SendMessageBatchV1(r)
|
||||
errorResult := response.GetResult().(models.ErrorResult)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, status)
|
||||
assert.Equal(t, expected, errorResult)
|
||||
|
||||
}
|
||||
|
||||
func TestSendMessageBatchV1_Error_transformer(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, _ := SendMessageBatchV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"shared-sqs/app/test"
|
||||
|
||||
"shared-sqs/app/fixtures"
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSendMessageV1_Success(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
sendMessageRequest_success := models.SendMessageRequest{
|
||||
QueueUrl: "http://localhost:4200/new-queue-1",
|
||||
MessageBody: "Test Message",
|
||||
}
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.SendMessageRequest)
|
||||
*v = sendMessageRequest_success
|
||||
return true
|
||||
}
|
||||
|
||||
q := &models.Queue{
|
||||
Name: "new-queue-1",
|
||||
MaximumMessageSize: 1024,
|
||||
}
|
||||
models.SyncQueues.Queues["new-queue-1"] = q
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
status, response := SendMessageV1(r)
|
||||
|
||||
// Check the queue
|
||||
assert.Equal(t, 1, len(q.Messages))
|
||||
msg := q.Messages[0]
|
||||
assert.Equal(t, "Test Message", string(msg.MessageBody))
|
||||
|
||||
// Check the response
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
sendMessageResponse, ok := response.(models.SendMessageResponse)
|
||||
assert.True(t, ok)
|
||||
assert.NotEmpty(t, sendMessageResponse.Result.MD5OfMessageBody)
|
||||
// No FIFO Sequence
|
||||
assert.Empty(t, sendMessageResponse.Result.SequenceNumber)
|
||||
}
|
||||
|
||||
func TestSendMessageV1_Success_FIFOQueue(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
sendMessageRequest_success := models.SendMessageRequest{
|
||||
QueueUrl: "http://localhost:4200/new-queue-1",
|
||||
MessageBody: "Test Message",
|
||||
}
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.SendMessageRequest)
|
||||
*v = sendMessageRequest_success
|
||||
return true
|
||||
}
|
||||
|
||||
q := &models.Queue{
|
||||
Name: "new-queue-1",
|
||||
MaximumMessageSize: 1024,
|
||||
IsFIFO: true,
|
||||
}
|
||||
models.SyncQueues.Queues["new-queue-1"] = q
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
status, response := SendMessageV1(r)
|
||||
|
||||
// Check the queue
|
||||
assert.Equal(t, 1, len(q.Messages))
|
||||
msg := q.Messages[0]
|
||||
assert.Equal(t, "Test Message", string(msg.MessageBody))
|
||||
|
||||
// Check the response
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
sendMessageResponse, ok := response.(models.SendMessageResponse)
|
||||
assert.True(t, ok)
|
||||
assert.NotEmpty(t, sendMessageResponse.Result.MD5OfMessageBody)
|
||||
// Should have FIFO Sequence
|
||||
assert.NotEmpty(t, sendMessageResponse.Result.SequenceNumber)
|
||||
}
|
||||
|
||||
func TestSendMessageV1_Success_Deduplication(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
sendMessageRequest_success := models.SendMessageRequest{
|
||||
QueueUrl: "http://localhost:4200/new-queue-1",
|
||||
MessageBody: "Test Message",
|
||||
MessageDeduplicationId: "1",
|
||||
}
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.SendMessageRequest)
|
||||
*v = sendMessageRequest_success
|
||||
return true
|
||||
}
|
||||
|
||||
q := &models.Queue{
|
||||
Name: "new-queue-1",
|
||||
MaximumMessageSize: 1024,
|
||||
IsFIFO: true,
|
||||
EnableDuplicates: true,
|
||||
Duplicates: make(map[string]time.Time),
|
||||
}
|
||||
models.SyncQueues.Queues["new-queue-1"] = q
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
status, _ := SendMessageV1(r)
|
||||
|
||||
// Check the queue
|
||||
assert.Equal(t, 1, len(q.Messages))
|
||||
// Check the response
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
|
||||
// Send the same message (have DeduplicationId)
|
||||
status, _ = SendMessageV1(r)
|
||||
// Response is "success"
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
// Only 1 message should be in the queue
|
||||
assert.Equal(t, 1, len(q.Messages))
|
||||
}
|
||||
|
||||
func TestSendMessageV1_request_transformer_error(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, _ := SendMessageV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
}
|
||||
|
||||
func TestSendMessageV1_MaximumMessageSize_MessageTooBig(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
sendMessageRequest_success := models.SendMessageRequest{
|
||||
QueueUrl: "http://localhost:4200/new-queue-1",
|
||||
MessageBody: "Test Message",
|
||||
}
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.SendMessageRequest)
|
||||
*v = sendMessageRequest_success
|
||||
return true
|
||||
}
|
||||
|
||||
q := &models.Queue{
|
||||
Name: "new-queue-1",
|
||||
MaximumMessageSize: 1,
|
||||
}
|
||||
models.SyncQueues.Queues["new-queue-1"] = q
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
status, response := SendMessageV1(r)
|
||||
|
||||
// Check the response
|
||||
assert.Equal(t, http.StatusBadRequest, status)
|
||||
errorResponse, ok := response.(models.ErrorResponse)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "MessageTooBig", errorResponse.Result.Type)
|
||||
}
|
||||
|
||||
func TestSendMessageV1_POST_QueueNonExistant(t *testing.T) {
|
||||
models.CurrentEnvironment = fixtures.LOCAL_ENVIRONMENT
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
sendMessageRequest_success := models.SendMessageRequest{
|
||||
QueueUrl: "http://localhost:4200/new-queue-1",
|
||||
MessageBody: "Test Message",
|
||||
}
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.SendMessageRequest)
|
||||
*v = sendMessageRequest_success
|
||||
return true
|
||||
}
|
||||
|
||||
// No test queue is added to app.SyncQueues
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
status, response := SendMessageV1(r)
|
||||
|
||||
// Check the status code is what we expect.
|
||||
assert.Equal(t, http.StatusBadRequest, status)
|
||||
|
||||
// Check the response body is what we expect.
|
||||
errorResponse, ok := response.(models.ErrorResponse)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, "Not Found", errorResponse.Result.Type)
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
|
||||
"shared-sqs/app/interfaces"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func SetQueueAttributesV1(req *http.Request) (int, interfaces.AbstractResponseBody) {
|
||||
requestBody := models.NewSetQueueAttributesRequest()
|
||||
ok := utils.REQUEST_TRANSFORMER(requestBody, req, false)
|
||||
if !ok {
|
||||
log.Error("Invalid Request - GetQueueAttributesV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
if requestBody.QueueUrl == "" {
|
||||
log.Error("Missing QueueUrl - GetQueueAttributesV1")
|
||||
return utils.CreateErrorResponseV1("InvalidParameterValue", true)
|
||||
}
|
||||
|
||||
// NOTE: I tore out the handling for devining the url from a param. I can't find documentation that
|
||||
// that is valid any longer.
|
||||
uriSegments := strings.Split(requestBody.QueueUrl, "/")
|
||||
queueName := uriSegments[len(uriSegments)-1]
|
||||
|
||||
log.Infof("Set Queue QueueAttributes: %s", queueName)
|
||||
models.SyncQueues.Lock()
|
||||
defer models.SyncQueues.Unlock()
|
||||
queue, ok := models.SyncQueues.Queues[queueName]
|
||||
if !ok {
|
||||
log.Warningf("Get Queue URL: %s, queue does not exist!!!", queueName)
|
||||
return utils.CreateErrorResponseV1("QueueNotFound", true)
|
||||
}
|
||||
if err := setQueueAttributesV1(queue, requestBody.Attributes); err != nil {
|
||||
return utils.CreateErrorResponseV1(err.Error(), true)
|
||||
}
|
||||
|
||||
respStruct := models.SetQueueAttributesResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
return http.StatusOK, respStruct
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package gosqs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"shared-sqs/app/test"
|
||||
|
||||
"shared-sqs/app/conf"
|
||||
"shared-sqs/app/fixtures"
|
||||
"shared-sqs/app/interfaces"
|
||||
"shared-sqs/app/models"
|
||||
"shared-sqs/app/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSetQueueAttributesV1_success_multiple_attributes(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.SetQueueAttributesRequest)
|
||||
*v = fixtures.SetQueueAttributesRequest
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := SetQueueAttributesV1(r)
|
||||
|
||||
expectedResponse := models.SetQueueAttributesResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, expectedResponse, response)
|
||||
|
||||
actualQueue := models.SyncQueues.Queues["unit-queue1"]
|
||||
assert.Equal(t, 5, actualQueue.VisibilityTimeout)
|
||||
assert.Equal(t, 4, actualQueue.ReceiveMessageWaitTimeSeconds)
|
||||
assert.Equal(t, 1, actualQueue.DelaySeconds)
|
||||
assert.Equal(t, 2, actualQueue.MaximumMessageSize)
|
||||
assert.Equal(t, 3, actualQueue.MessageRetentionPeriod)
|
||||
}
|
||||
|
||||
func TestSetQueueAttributesV1_success_single_attribute(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.SetQueueAttributesRequest)
|
||||
*v = models.SetQueueAttributesRequest{
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "unit-queue1"),
|
||||
Attributes: models.QueueAttributes{
|
||||
VisibilityTimeout: 5,
|
||||
},
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, response := SetQueueAttributesV1(r)
|
||||
|
||||
expectedResponse := models.SetQueueAttributesResponse{
|
||||
Xmlns: models.BaseXmlns,
|
||||
Metadata: models.BaseResponseMetadata,
|
||||
}
|
||||
assert.Equal(t, http.StatusOK, code)
|
||||
assert.Equal(t, expectedResponse, response)
|
||||
|
||||
actualQueue := models.SyncQueues.Queues["unit-queue1"]
|
||||
assert.Equal(t, 5, actualQueue.VisibilityTimeout)
|
||||
assert.Equal(t, 0, actualQueue.ReceiveMessageWaitTimeSeconds)
|
||||
assert.Equal(t, 0, actualQueue.DelaySeconds)
|
||||
assert.Equal(t, 0, actualQueue.MaximumMessageSize)
|
||||
assert.Equal(t, 345600, actualQueue.MessageRetentionPeriod)
|
||||
}
|
||||
|
||||
func TestSetQueueAttributesV1_invalid_request_body(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, _ := SetQueueAttributesV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
}
|
||||
|
||||
func TestSetQueueAttributesV1_missing_queue_url(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.SetQueueAttributesRequest)
|
||||
*v = models.SetQueueAttributesRequest{
|
||||
Attributes: models.QueueAttributes{},
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, _ := SetQueueAttributesV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
}
|
||||
|
||||
func TestSetQueueAttributesV1_missing_expected_queue(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.SetQueueAttributesRequest)
|
||||
*v = models.SetQueueAttributesRequest{
|
||||
QueueUrl: "garbage",
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, _ := SetQueueAttributesV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
}
|
||||
|
||||
func TestSetQueueAttributesV1_invalid_redrive_queue(t *testing.T) {
|
||||
conf.LoadYamlConfig("../conf/mock-data/mock-config.yaml", "BaseUnitTests")
|
||||
defer func() {
|
||||
models.ResetApp()
|
||||
utils.REQUEST_TRANSFORMER = utils.TransformRequest
|
||||
}()
|
||||
|
||||
utils.REQUEST_TRANSFORMER = func(resultingStruct interfaces.AbstractRequestBody, req *http.Request, emptyRequestValid bool) (success bool) {
|
||||
v := resultingStruct.(*models.SetQueueAttributesRequest)
|
||||
*v = models.SetQueueAttributesRequest{
|
||||
QueueUrl: fmt.Sprintf("%s/%s", fixtures.BASE_URL, "unit-queue1"),
|
||||
Attributes: models.QueueAttributes{
|
||||
RedrivePolicy: models.RedrivePolicy{
|
||||
MaxReceiveCount: 100,
|
||||
DeadLetterTargetArn: fmt.Sprintf("arn:aws:sqs:us-east-1:100010001000:%s", "garbage"),
|
||||
},
|
||||
},
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
_, r := test.GenerateRequestInfo("POST", "/", nil, true)
|
||||
code, _ := SetQueueAttributesV1(r)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, code)
|
||||
}
|
||||
Reference in New Issue
Block a user