Skip to content

Commit 310c1ca

Browse files
committed
fix(sqs/htfifo): PR #681 round 2 — FIFO-only PartitionCount + test cleanup
Three items from the round-2 Claude review on PR #681: (1) Bug (latent, live in PR 5): validatePartitionConfig only guarded FifoThroughputLimit / DeduplicationScope as FIFO-only, not PartitionCount > 1. A Standard queue with PartitionCount=2 would slip past the validator after PR 5 lifts the dormancy gate. Added a guard inside the !meta.IsFIFO block: PartitionCount > 1 → InvalidAttributeValue("PartitionCount > 1 is only valid on FIFO queues"). PartitionCount 0 / 1 stay accepted on Standard (both mean "single-partition layout"). Test TestValidatePartitionConfig_StandardQueueRejectsHTFIFOAttrs extended to cover PartitionCount=2/4/8/16/32 → reject and PartitionCount=0/1 → accept. (2) Low: TestSQSServer_HTFIFO_RejectsQueueScopedDedupOnPartitioned had two contradictory comments. The function-level doc said "Test sets PartitionCount=1 to bypass dormancy" but the test actually sends PartitionCount=2; the inline comment claimed "dormancy fires first" but validatePartitionConfig (inside parseAttributesIntoMeta) runs before validatePartitionDormancyGate, so the cross-attr rule fires first. Rewrote the doc to describe the actual control-plane order (validatePartitionConfig → cross-attr rejection → wire 400) and removed the misleading "dormancy fires first" assertion. (3) Nit: replaced the custom 11-line contains/indexOf helpers with strings.Contains. Identical behaviour, idiomatic. All findings noted in the round-2 Claude review on PR #681.
1 parent 288b06e commit 310c1ca

3 files changed

Lines changed: 38 additions & 33 deletions

File tree

adapter/sqs_partitioning.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,17 @@ func validatePartitionConfig(meta *sqsQueueMeta) error {
142142
}
143143
}
144144
if !meta.IsFIFO {
145+
// PartitionCount > 1 only makes sense on FIFO queues (HT-FIFO
146+
// is by definition a FIFO feature). Without this guard a
147+
// Standard queue with PartitionCount=2 would slip past the
148+
// validator once PR 5 lifts the dormancy gate (Claude review
149+
// on PR #681 round 2 caught this). PartitionCount=0 and 1
150+
// are accepted because both mean "single-partition layout"
151+
// which is valid on Standard queues.
152+
if meta.PartitionCount > 1 {
153+
return newSQSAPIError(http.StatusBadRequest, sqsErrInvalidAttributeValue,
154+
"PartitionCount > 1 is only valid on FIFO queues")
155+
}
145156
if meta.FifoThroughputLimit != "" {
146157
return newSQSAPIError(http.StatusBadRequest, sqsErrInvalidAttributeValue,
147158
"FifoThroughputLimit is only valid on FIFO queues")

adapter/sqs_partitioning_integration_test.go

Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package adapter
22

33
import (
44
"net/http"
5+
"strings"
56
"testing"
67
)
78

@@ -32,7 +33,7 @@ func TestSQSServer_HTFIFO_DormancyGate_RejectsPartitionedCreate(t *testing.T) {
3233
t.Fatalf("PartitionCount=%s: __type=%q (expected InvalidAttributeValue)", n, got)
3334
}
3435
msg, _ := out["message"].(string)
35-
if msg == "" || !contains(msg, "not yet enabled") {
36+
if msg == "" || !strings.Contains(msg, "not yet enabled") {
3637
t.Fatalf("PartitionCount=%s: message %q must mention the gate reason", n, msg)
3738
}
3839
}
@@ -80,7 +81,7 @@ func TestSQSServer_HTFIFO_RejectsNonPowerOfTwoPartitionCount(t *testing.T) {
8081
if status != http.StatusBadRequest {
8182
t.Fatalf("PartitionCount=3 must reject: status %d", status)
8283
}
83-
if msg, _ := out["message"].(string); msg == "" || !contains(msg, "power of two") {
84+
if msg, _ := out["message"].(string); msg == "" || !strings.Contains(msg, "power of two") {
8485
t.Fatalf("expected 'power of two' in message, got %q", msg)
8586
}
8687
}
@@ -109,24 +110,24 @@ func TestSQSServer_HTFIFO_RejectsHTFIFOAttrsOnStandardQueue(t *testing.T) {
109110

110111
// TestSQSServer_HTFIFO_RejectsQueueScopedDedupOnPartitioned pins
111112
// the §3.2 cross-attribute control-plane gate at the wire layer.
112-
// {PartitionCount > 1, DeduplicationScope = "queue"} would land
113-
// fine if dormancy were lifted but the validator rejects it before
114-
// dormancy runs. Test sets PartitionCount=1 to bypass dormancy and
115-
// exercise the cross-attr rule alone — when dormancy is lifted in
116-
// PR 5 the equivalent test with PartitionCount > 1 will exercise
117-
// the same path end-to-end.
113+
// {PartitionCount > 1, DeduplicationScope = "queue"} is rejected by
114+
// validatePartitionConfig (the schema validator) which runs inside
115+
// parseAttributesIntoMeta — that is, BEFORE validatePartitionDormancyGate
116+
// runs in createQueue. So the cross-attr rejection is what the wire
117+
// layer sees today, even though the dormancy gate would also reject
118+
// the same input on its own. After PR 5 lifts the dormancy gate the
119+
// cross-attr rule remains the sole rejection path.
120+
//
121+
// The test only checks the 400 status to stay agnostic about which
122+
// validator fires first — both are correct behaviour, and a future
123+
// reordering of the createQueue control flow does not need to break
124+
// this test.
118125
func TestSQSServer_HTFIFO_RejectsQueueScopedDedupOnPartitioned(t *testing.T) {
119126
t.Parallel()
120127
nodes, _, _ := createNode(t, 1)
121128
defer shutdown(nodes)
122129
node := sqsLeaderNode(t, nodes)
123130

124-
// Direct unit-test of the validator: dormancy gate runs after
125-
// the schema validator, but in the wire path PartitionCount > 1
126-
// is rejected by dormancy first, so we cover the cross-attr
127-
// rejection via the unit test in sqs_partitioning_test.go and
128-
// the wire test exercises only the dormancy path while PR 2-4
129-
// are in production.
130131
status, out := callSQS(t, node, sqsCreateQueueTarget, map[string]any{
131132
"QueueName": "htfifo-bad-dedup.fifo",
132133
"Attributes": map[string]string{
@@ -138,9 +139,6 @@ func TestSQSServer_HTFIFO_RejectsQueueScopedDedupOnPartitioned(t *testing.T) {
138139
if status != http.StatusBadRequest {
139140
t.Fatalf("PartitionCount=2 + DeduplicationScope=queue must reject: status %d body %v", status, out)
140141
}
141-
// During PR 2-4 the dormancy gate fires first (PartitionCount > 1);
142-
// after PR 5 lifts the gate, the cross-attr rule fires instead.
143-
// Either rejection is correct so the test only checks the 400.
144142
}
145143

146144
// TestSQSServer_HTFIFO_ImmutabilitySetQueueAttributesRejects pins
@@ -269,19 +267,3 @@ func mustCreateFIFOWithThroughputLimit(t *testing.T, node Node, name, limit stri
269267
url, _ := out["QueueUrl"].(string)
270268
return url
271269
}
272-
273-
// contains is a tiny helper used by the dormancy-gate test to check
274-
// for a substring in the operator-facing message without pulling in
275-
// strings just for one call.
276-
func contains(s, sub string) bool {
277-
return len(s) >= len(sub) && indexOf(s, sub) >= 0
278-
}
279-
280-
func indexOf(s, sub string) int {
281-
for i := 0; i+len(sub) <= len(s); i++ {
282-
if s[i:i+len(sub)] == sub {
283-
return i
284-
}
285-
}
286-
return -1
287-
}

adapter/sqs_partitioning_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,22 @@ func TestValidatePartitionConfig_RejectsAboveMax(t *testing.T) {
163163
// the §3.2 FIFO-only rule: HT-FIFO attributes on a non-FIFO queue
164164
// reject with InvalidAttributeValue. Setting them silently on a
165165
// Standard queue would advertise unsupported behaviour.
166+
//
167+
// PartitionCount > 1 is also FIFO-only (Claude review on PR #681
168+
// round 2 caught the gap) — without the guard a Standard queue
169+
// with PartitionCount=2 would slip past the validator after PR 5
170+
// lifts the dormancy gate. PartitionCount 0/1 are still accepted
171+
// on Standard queues because both mean "single-partition layout".
166172
func TestValidatePartitionConfig_StandardQueueRejectsHTFIFOAttrs(t *testing.T) {
167173
t.Parallel()
168174
require.Error(t, validatePartitionConfig(&sqsQueueMeta{IsFIFO: false, FifoThroughputLimit: htfifoThroughputPerQueue}))
169175
require.Error(t, validatePartitionConfig(&sqsQueueMeta{IsFIFO: false, DeduplicationScope: htfifoDedupeScopeMessageGroup}))
176+
for _, n := range []uint32{2, 4, 8, 16, 32} {
177+
require.Error(t, validatePartitionConfig(&sqsQueueMeta{IsFIFO: false, PartitionCount: n}),
178+
"PartitionCount=%d on Standard queue must reject", n)
179+
}
180+
require.NoError(t, validatePartitionConfig(&sqsQueueMeta{IsFIFO: false, PartitionCount: 0}))
181+
require.NoError(t, validatePartitionConfig(&sqsQueueMeta{IsFIFO: false, PartitionCount: 1}))
170182
require.NoError(t, validatePartitionConfig(&sqsQueueMeta{IsFIFO: true, FifoThroughputLimit: htfifoThroughputPerMessageGroupID, PartitionCount: 8}))
171183
}
172184

0 commit comments

Comments
 (0)