// app/gosqs/delete_middle_test.go // Тест P1.2: удаление сообщения из СЕРЕДИНЫ очереди не ломает остальные — // индексы receipt/uuid корректны (tombstone), удалённое не возвращается, // оставшиеся находятся по своим ReceiptHandle. package gosqs import ( "net/http" "testing" "shared-sqs/app/models" ) func TestDeleteMessageV1_MiddleKeepsOthersReceivable(t *testing.T) { resetTestEnv(t) tnt := testTenant("SSAK-aaa") injectRequest(t, func(v interface{}) { v.(*models.CreateQueueRequest).QueueName = "q" }) if code, _ := CreateQueueV1(reqWithTenant("/", tnt)); code != http.StatusOK { t.Fatal("create failed") } q := models.SyncQueues.Queues["SSAK-aaa:q"] q.MaximumMessageSize = 262144 // Три сообщения. for _, body := range []string{"m1", "m2", "m3"} { b := body injectRequest(t, func(v interface{}) { r := v.(*models.SendMessageRequest) r.QueueUrl = "http://x/tenant/q" r.MessageBody = b }) if code, _ := SendMessageV1(reqWithTenant("/", tnt)); code != http.StatusOK { t.Fatal("send failed") } } // Получаем все три разом. injectRequest(t, func(v interface{}) { r := v.(*models.ReceiveMessageRequest) r.QueueUrl = "http://x/tenant/q" r.MaxNumberOfMessages = 10 }) _, resp := ReceiveMessageV1(reqWithTenant("/", tnt)) rr := resp.(models.ReceiveMessageResponse) if len(rr.Result.Messages) != 3 { t.Fatalf("receive вернул %d сообщений, want 3", len(rr.Result.Messages)) } // Удаляем СРЕДНЕЕ. middle := rr.Result.Messages[1] injectRequest(t, func(v interface{}) { r := v.(*models.DeleteMessageRequest) r.QueueUrl = "http://x/tenant/q" r.ReceiptHandle = middle.ReceiptHandle }) if code, _ := DeleteMessageV1(reqWithTenant("/", tnt)); code != http.StatusOK { t.Fatal("delete failed") } if q.LiveCount() != 2 { t.Fatalf("LiveCount = %d, want 2", q.LiveCount()) } if _, ok := q.IndexOfUUID(middle.MessageId); ok { t.Fatal("удалённое сообщение осталось в uuid-индексе") } // Оставшиеся по-прежнему in-flight и находятся по своим handle (индексы не «съехали»). for _, m := range []*models.ResultMessage{rr.Result.Messages[0], rr.Result.Messages[2]} { if _, ok := q.IndexOfReceipt(m.ReceiptHandle); !ok { t.Fatalf("handle оставшегося сообщения потерян: %s", m.ReceiptHandle) } } // Возвращаем видимость оставшимся и получаем их снова — удалённого быть не должно. for _, m := range []*models.ResultMessage{rr.Result.Messages[0], rr.Result.Messages[2]} { handle := m.ReceiptHandle injectRequest(t, func(v interface{}) { r := v.(*models.ChangeMessageVisibilityRequest) r.QueueUrl = "http://x/tenant/q" r.ReceiptHandle = handle r.VisibilityTimeout = 0 }) if code, _ := ChangeMessageVisibilityV1(reqWithTenant("/", tnt)); code != http.StatusOK { t.Fatal("change visibility failed") } } injectRequest(t, func(v interface{}) { r := v.(*models.ReceiveMessageRequest) r.QueueUrl = "http://x/tenant/q" r.MaxNumberOfMessages = 10 }) _, resp2 := ReceiveMessageV1(reqWithTenant("/", tnt)) rr2 := resp2.(models.ReceiveMessageResponse) if len(rr2.Result.Messages) != 2 { t.Fatalf("повторный receive вернул %d, want 2", len(rr2.Result.Messages)) } for _, m := range rr2.Result.Messages { if m.MessageId == middle.MessageId { t.Fatal("удалённое сообщение вернулось в receive") } } }