Repository: goptics/varmq
Pull request #17: Feat/improve ext queue
Author: login: fahimfaisaal
Created: 2025-05-19T10:03:37Z | Merged: 2025-05-19T12:49:15Z

- **New Features**
  - Added explicit success/failure indication when adding jobs to queues, with methods now returning a boolean alongside the job.
  - Introduced a method to retrieve the current job count in group jobs.
  - Removed `PQitem` and merged it with `Item` for more simplicity

- **Bug Fixes**
  - Improved error handling for batch job additions, ensuring only successfully enqueued jobs are processed.

- **Refactor**
  - Unified and simplified type usage for buffer sizes and item types across queue and priority queue operations.
  - Updated interface and method signatures for consistency and clarity.
  - Removed panic check on worker start to allow more flexible queue binding.

- **Tests**
  - Added explicit tests for queue resizing under various conditions, verifying capacity, size, and element order preservation.
  - Introduced comprehensive unit tests covering queue operations, job processing, and batch job handling with result verification.



Diff:
diff --git a/bench_test.go b/bench_test.go
index 3824dc1..84ae8dd 100644
--- a/bench_test.go
+++ b/bench_test.go
@@ -24,7 +24,9 @@ func BenchmarkQueue_Operations(b *testing.B) {
 
 		b.ResetTimer()
 		for j := 0; j < b.N; j++ {
-			q.Add(j).Result()
+			if job, ok := q.Add(j); ok {
+				job.Result()
+			}
 		}
 	})
 
@@ -62,7 +64,9 @@ func BenchmarkQueue_ParallelOperations(b *testing.B) {
 		b.ResetTimer()
 		b.RunParallel(func(pb *testing.PB) {
 			for pb.Next() {
-				q.Add(1).Result()
+				if job, ok := q.Add(1); ok {
+					job.Result()
+				}
 			}
 		})
 	})
@@ -102,7 +106,9 @@ func BenchmarkPriorityQueue_Operations(b *testing.B) {
 
 		b.ResetTimer()
 		for i := 0; i < b.N; i++ {
-			q.Add(i, i%10).Result()
+			if job, ok := q.Add(i, i%10); ok {
+				job.Result()
+			}
 		}
 	})
 
@@ -113,9 +119,9 @@ func BenchmarkPriorityQueue_Operations(b *testing.B) {
 		q := worker.BindPriorityQueue()
 		defer q.WaitAndClose()
 
-		data := make([]PQItem[int], 1000) // Using a constant size of 1000 for testing
+		data := make([]Item[int], 1000) // Using a constant size of 1000 for testing
 		for i := range data {
-			data[i] = PQItem[int]{Value: i, Priority: i % 10}
+			data[i] = Item[int]{Value: i, Priority: i % 10}
 		}
 
 		b.ResetTimer()
@@ -140,7 +146,9 @@ func BenchmarkPriorityQueue_ParallelOperations(b *testing.B) {
 		b.ResetTimer()
 		b.RunParallel(func(pb *testing.PB) {
 			for pb.Next() {
-				q.Add(1, 0).Result()
+				if job, ok := q.Add(1, 0); ok {
+					job.Result()
+				}
 			}
 		})
 	})
@@ -152,17 +160,18 @@ func BenchmarkPriorityQueue_ParallelOperations(b *testing.B) {
 		q := worker.BindPriorityQueue()
 		defer q.WaitAndClose()
 
-		data := make([]PQItem[int], 1000) // Using a constant size of 1000 for testing
+		data := make([]Item[int], 1000) // Using a constant size of 1000 for testing
 		for i := range data {
-			data[i] = PQItem[int]{Value: i, Priority: i % 10}
+			data[i] = Item[int]{Value: i, Priority: i % 10}
 		}
 
 		b.ResetTimer()
 		b.RunParallel(func(pb *testing.PB) {
 			for pb.Next() {
-				results, _ := q.AddAll(data).Results()
-				for range results {
-					// drain the channel
+				if results, err := q.AddAll(data).Results(); err == nil {
+					for range results {
+						// drain the channel
+					}
 				}
 			}
 		})
@@ -180,7 +189,9 @@ func BenchmarkVoidWorker_Operations(b *testing.B) {
 
 		b.ResetTimer()
 		for j := 0; j < b.N; j++ {
-			q.Add(j).Result()
+			if job, ok := q.Add(j); ok {
+				job.Result()
+			}
 		}
 	})
 
@@ -198,9 +209,10 @@ func BenchmarkVoidWorker_Operations(b *testing.B) {
 
 		b.ResetTimer()
 		for i := 0; i < b.N; i++ {
-			results, _ := q.AddAll(data).Results()
-			for range results {
-				// drain the channel
+			if results, err := q.AddAll(data).Results(); err == nil {
+				for range results {
+					// drain the channel
+				}
 			}
 		}
 	})
@@ -218,7 +230,9 @@ func BenchmarkVoidWorker_ParallelOperations(b *testing.B) {
 		b.ResetTimer()
 		b.RunParallel(func(pb *testing.PB) {
 			for pb.Next() {
-				q.Add(1).Result()
+				if job, ok := q.Add(1); ok {
+					job.Result()
+				}
 			}
 		})
 	})
diff --git a/group_job.go b/group_job.go
index ca85a99..14cd3d4 100644
--- a/group_job.go
+++ b/group_job.go
@@ -3,26 +3,44 @@ package varmq
 import (
 	"fmt"
 	"sync"
+	"sync/atomic"
 )
 
 // groupJob represents a job that can be used in a group.
 type groupJob[T, R any] struct {
 	*job[T, R]
-	wg *sync.WaitGroup
+	wg  *sync.WaitGroup
+	len *groupLen
+}
+
+type groupLen struct {
+	len atomic.Uint32
+}
+
+func (gl *groupLen) Add() {
+	gl.len.Add(1)
+}
+
+func (gl *groupLen) Sub() {
+	gl.len.Add(^uint32(0))
+}
+
+func (gl *groupLen) Get() int {
+	return int(gl.len.Load())
 }
 
 const groupIdPrefixed = "g:"
 
-func newGroupJob[T, R any](bufferSize uint32) *groupJob[T, R] {
+func newGroupJob[T, R any](bufferSize int) *groupJob[T, R] {
 	gj := &groupJob[T, R]{
 		job: &job[T, R]{
 			resultChannel: newResultChannel[R](bufferSize),
 		},
-		wg: new(sync.WaitGroup),
+		wg:  new(sync.WaitGroup),
+		len: new(groupLen),
 	}
 
-	gj.wg.Add(int(bufferSize))
-
+	gj.wg.Add(bufferSize)
 	return gj
 }
 
@@ -31,13 +49,15 @@ func generateGroupId(id string) string {
 }
 
 func (gj *groupJob[T, R]) NewJob(data T, config jobConfigs) *groupJob[T, R] {
+	gj.len.Add()
 	return &groupJob[T, R]{
 		job: &job[T, R]{
 			id:            generateGroupId(config.Id),
 			Input:         data,
 			resultChannel: gj.resultChannel,
 		},
-		wg: gj.wg,
+		wg:  gj.wg,
+		len: gj.len,
 	}
 }
 
@@ -60,6 +80,10 @@ func (gj *groupJob[T, R]) Results() (<-chan Result[R], error) {
 	return ch, nil
 }
 
+func (gj *groupJob[T, R]) Len() int {
+	return gj.len.Get()
+}
+
 // Drain discards the job's result and error values asynchronously.
 // This is useful when you no longer need the results but want to ensure
 // the channels are emptied.
@@ -92,5 +116,6 @@ func (gj *groupJob[T, R]) close() error {
 	gj.wg.Done()
 	gj.Ack()
 	gj.ChangeStatus(closed)
+	gj.len.Sub()
 	return nil
 }
diff --git a/group_job_test.go b/group_job_test.go
index fc5a89e..f858dd4 100644
--- a/group_job_test.go
+++ b/group_job_test.go
@@ -10,7 +10,7 @@ import (
 func TestGroupJob(t *testing.T) {
 	t.Run("group job creation with newGroupJob", func(t *testing.T) {
 		// Create a new group job with buffer size 3
-		bufferSize := uint32(3)
+		bufferSize := 3
 		gj := newGroupJob[string, int](bufferSize)
 
 		assert := assert.New(t)
@@ -36,7 +36,7 @@ func TestGroupJob(t *testing.T) {
 
 	t.Run("creating new job in a group", func(t *testing.T) {
 		// Create a new group job
-		bufferSize := uint32(3)
+		bufferSize := 3
 		gj := newGroupJob[string, int](bufferSize)
 
 		// Create a job within the group
@@ -56,12 +56,12 @@ func TestGroupJob(t *testing.T) {
 
 	t.Run("closing a group job", func(t *testing.T) {
 		// Create a group job with small buffer to avoid wait group blocking
-		bufferSize := uint32(1)
+		bufferSize := 1
 		gj := newGroupJob[string, int](bufferSize)
 
 		// Manually reduce the wait group count to match our single Close() call
 		// This prevents the wait group from blocking indefinitely
-		gj.wg.Add(-int(bufferSize) + 1)
+		gj.wg.Add(-bufferSize + 1)
 
 		assert := assert.New(t)
 
@@ -79,7 +79,7 @@ func TestGroupJob(t *testing.T) {
 
 	t.Run("job status transitions in a group job", func(t *testing.T) {
 		// Create a new group job
-		bufferSize := uint32(1)
+		bufferSize := 1
 		gj := newGroupJob[string, int](bufferSize)
 
 		// Reduce wait group count to prevent deadlock when testing
diff --git a/internal/collections/queue_test.go b/internal/collections/queue_test.go
index 61cfaaa..9b638ac 100644
--- a/internal/collections/queue_test.go
+++ b/internal/collections/queue_test.go
@@ -165,4 +165,117 @@ func TestQueue(t *testing.T) {
 			assert.Equal(i, val, "remaining elements should be in order after shrinking")
 		}
 	})
+
+	// TestQueueResize explicitly tests the resize method
+	t.Run("ExplicitResizeMethod", func(t *testing.T) {
+		t.Run("Empty Queue", func(t *testing.T) {
+			assert := assert.New(t)
+			q := NewQueue[int]()
+			
+			// Explicitly call resize
+			q.resize(50)
+			
+			// Verify the queue after resize
+			assert.Equal(50, len(q.elements), "Capacity should be 50 after resize")
+			assert.Equal(0, q.size, "Size should remain 0")
+			assert.Equal(0, q.front, "Front should be reset to 0")
+		})
+		
+		t.Run("Queue With Elements", func(t *testing.T) {
+			assert := assert.New(t)
+			q := NewQueue[int]()
+			
+			// Add some elements
+			for i := 1; i <= 5; i++ {
+				q.Enqueue(i)
+			}
+			
+			// Explicitly call resize
+			q.resize(200)
+			
+			// Verify the queue after resize
+			assert.Equal(200, len(q.elements), "Capacity should be 200 after resize")
+			assert.Equal(5, q.size, "Size should remain 5")
+			assert.Equal(0, q.front, "Front should be reset to 0")
+			
+			// Check that elements are preserved in order
+			for i := 1; i <= 5; i++ {
+				val, ok := q.Dequeue()
+				assert.True(ok)
+				assert.Equal(i, val, "Elements should be preserved in order after resize")
+			}
+		})
+		
+		t.Run("Circular Buffer Wrap-Around", func(t *testing.T) {
+			assert := assert.New(t)
+			q := NewQueue[int]()
+			
+			// Add some elements
+			for i := 1; i <= 5; i++ {
+				q.Enqueue(i)
+			}
+			
+			// Remove a few to advance the front index
+			for i := 1; i <= 3; i++ {
+				val, _ := q.Dequeue()
+				assert.Equal(i, val)
+			}
+			
+			// Add more elements to cause wrap-around
+			for i := 6; i <= 8; i++ {
+				q.Enqueue(i)
+			}
+			
+			// At this point, elements should be [4,5,6,7,8] with front at some index > 0
+			frontBeforeResize := q.front
+			assert.Equal(5, q.size)
+			assert.Greater(frontBeforeResize, 0, "Front should be advanced")
+			
+			// Now explicitly call resize
+			q.resize(10)
+			
+			// Verify the queue after resize
+			assert.Equal(10, len(q.elements), "Capacity should be 10 after resize")
+			assert.Equal(5, q.size, "Size should remain 5")
+			assert.Equal(0, q.front, "Front should be reset to 0")
+			
+			// The elements should now be in contiguous order at the beginning of the slice
+			expected := []int{4, 5, 6, 7, 8}
+			for i, exp := range expected {
+				assert.Equal(exp, q.elements[i], "Element at index %d should be %d", i, exp)
+			}
+			
+			// Verify FIFO behavior is maintained
+			for _, exp := range expected {
+				val, ok := q.Dequeue()
+				assert.True(ok)
+				assert.Equal(exp, val, "Elements should be dequeued in correct order after resize")
+			}
+		})
+		
+		t.Run("Resize To Size Equal To Element Count", func(t *testing.T) {
+			assert := assert.New(t)
+			q := NewQueue[int]()
+			
+			// Add some elements
+			for i := 1; i <= 5; i++ {
+				q.Enqueue(i)
+			}
+			
+			// Resize to exactly the size of the queue
+			q.resize(5)
+			
+			// Verify the queue after resize
+			assert.Equal(5, len(q.elements), "Capacity should equal size after resize")
+			assert.Equal(5, q.size, "Size should remain 5")
+			assert.Equal(0, q.front, "Front should be reset to 0")
+			
+			// Verify elements are still correct
+			for i := 1; i <= 5; i++ {
+				val, ok := q.Dequeue()
+				assert.True(ok)
+				assert.Equal(i, val, "Elements should be dequeued in correct order after tight resize")
+			}
+		})
+	})
 }
diff --git a/persistent.go b/persistent.go
index 8475ac5..fe982de 100644
--- a/persistent.go
+++ b/persistent.go
@@ -24,21 +24,24 @@ func newPersistentQueue[T, R any](w *worker[T, R], pq IPersistentQueue) Persiste
 // It requires a job ID to be provided in the job config for persistence
 // It will panic if no job ID is provided
 // Returns an EnqueuedJob that can be used to track the job's status and result
-func (q *persistentQueue[T, R]) Add(data T, configs ...JobConfigFunc) EnqueuedJob[R] {
+func (q *persistentQueue[T, R]) Add(data T, configs ...JobConfigFunc) (EnqueuedJob[R], bool) {
 	jobConfig := withRequiredJobId(loadJobConfigs(q.configs, configs...))
 
 	j := newJob[T, R](data, jobConfig)
-	val, _ := j.Json()
+	val, err := j.Json()
+	if err != nil {
+		return nil, false
+	}
 	j.SetInternalQueue(q.internalQueue)
 
 	if ok := q.internalQueue.Enqueue(val); !ok {
 		j.close()
-		return j
+		return nil, false
 	}
 
 	q.postEnqueue(j)
 
-	return j
+	return j, true
 }
 
 // AddAll adds multiple jobs to the persistent queue at once
@@ -46,13 +49,18 @@ func (q *persistentQueue[T, R]) Add(data T, configs ...JobConfigFunc) EnqueuedJo
 // Returns an EnqueuedGroupJob that can be used to track all jobs' statuses and results
 // Will panic if any job is missing an ID
 func (q *persistentQueue[T, R]) AddAll(items []Item[T]) EnqueuedGroupJob[R] {
-	groupJob := newGroupJob[T, R](uint32(len(items)))
+	l := len(items)
+	groupJob := newGroupJob[T, R](l)
 
 	for _, item := range items {
 		jConfigs := withRequiredJobId(loadJobConfigs(q.configs, WithJobId(item.ID)))
 
 		j := groupJob.NewJob(item.Value, jConfigs)
-		val, _ := j.Json()
+		val, err := j.Json()
+		if err != nil {
+			j.close()
+			continue
+		}
 		j.SetInternalQueue(q.internalQueue)
 
 		if ok := q.internalQueue.Enqueue(val); !ok {
diff --git a/persistent_priority.go b/persistent_priority.go
index 99939bb..b326a70 100644
--- a/persistent_priority.go
+++ b/persistent_priority.go
@@ -15,31 +15,38 @@ func newPersistentPriorityQueue[T, R any](worker *worker[T, R], pq IPersistentPr
 	}
 }
 
-func (q *persistentPriorityQueue[T, R]) Add(data T, priority int, configs ...JobConfigFunc) EnqueuedJob[R] {
+func (q *persistentPriorityQueue[T, R]) Add(data T, priority int, configs ...JobConfigFunc) (EnqueuedJob[R], bool) {
 	jobConfig := withRequiredJobId(loadJobConfigs(q.configs, configs...))
 
 	j := newJob[T, R](data, jobConfig)
-	val, _ := j.Json()
+	val, err := j.Json()
+	if err != nil {
+		return nil, false
+	}
 	j.SetInternalQueue(q.internalQueue)
 
 	if ok := q.internalQueue.Enqueue(val, priority); !ok {
 		j.close()
-		return j
+		return nil, false
 	}
 
 	q.postEnqueue(j)
 
-	return j
+	return j, true
 }
 
-func (q *persistentPriorityQueue[T, R]) AddAll(items []PQItem[T]) EnqueuedGroupJob[R] {
-	groupJob := newGroupJob[T, R](uint32(len(items)))
+func (q *persistentPriorityQueue[T, R]) AddAll(items []Item[T]) EnqueuedGroupJob[R] {
+	groupJob := newGroupJob[T, R](len(items))
 
 	for _, item := range items {
 		jConfigs := withRequiredJobId(loadJobConfigs(q.configs, WithJobId(item.ID)))
 
 		j := groupJob.NewJob(item.Value, jConfigs)
-		val, _ := j.Json()
+		val, err := j.Json()
+		if err != nil {
+			j.close()
+			continue
+		}
 		j.SetInternalQueue(q.internalQueue)
 
 		if ok := q.internalQueue.Enqueue(val, item.Priority); !ok {
diff --git a/priority.go b/priority.go
index f0882e2..6503ff5 100644
--- a/priority.go
+++ b/priority.go
@@ -5,23 +5,14 @@ type priorityQueue[T, R any] struct {
 	internalQueue IPriorityQueue
 }
 
-type PQItem[T any] struct {
-	// ID is an optional identifier for the item
-	ID string
-	// Value contains the actual data stored in the queue
-	Value T
-	// Priority determines the item's position in the queue
-	Priority int
-}
-
 type PriorityQueue[T, R any] interface {
 	IExternalQueue[T, R]
 	// Add adds a new Job with the given priority to the queue and returns a channel to receive the result.
 	// Time complexity: O(log n)
-	Add(data T, priority int, configs ...JobConfigFunc) EnqueuedJob[R]
+	Add(data T, priority int, configs ...JobConfigFunc) (EnqueuedJob[R], bool)
 	// AddAll adds multiple Jobs with the given priority to the queue and returns a channel to receive all responses.
 	// Time complexity: O(n log n) where n is the number of Jobs added
-	AddAll(items []PQItem[T]) EnqueuedGroupJob[R]
+	AddAll(data []Item[T]) EnqueuedGroupJob[R]
 }
 
 // NewPriorityQueue creates a new priorityQueue with the specified concurrency and worker function.
@@ -34,23 +25,31 @@ func newPriorityQueue[T, R any](worker *worker[T, R], pq IPriorityQueue) *priori
 	}
 }
 
-func (q *priorityQueue[T, R]) Add(data T, priority int, configs ...JobConfigFunc) EnqueuedJob[R] {
+func (q *priorityQueue[T, R]) Add(data T, priority int, configs ...JobConfigFunc) (EnqueuedJob[R], bool) {
 	j := newJob[T, R](data, loadJobConfigs(q.configs, configs...))
 
-	q.internalQueue.Enqueue(j, priority)
+	if ok := q.internalQueue.Enqueue(j, priority); !ok {
+		j.close()
+		return nil, false
+	}
+
 	q.postEnqueue(j)
 
-	return j
+	return j, true
 }
 
-func (q *priorityQueue[T, R]) AddAll(items []PQItem[T]) EnqueuedGroupJob[R] {
+func (q *priorityQueue[T, R]) AddAll(items []Item[T]) EnqueuedGroupJob[R] {
 	l := len(items)
-	groupJob := newGroupJob[T, R](uint32(l))
+	groupJob := newGroupJob[T, R](l)
 
 	for _, item := range items {
 		j := groupJob.NewJob(item.Value, loadJobConfigs(q.configs, WithJobId(item.ID)))
 
-		q.internalQueue.Enqueue(j, item.Priority)
+		if ok := q.internalQueue.Enqueue(j, item.Priority); !ok {
+			j.close()
+			continue
+		}
+
 		q.postEnqueue(j)
 	}
 
diff --git a/queue.go b/queue.go
index e64e033..3748b4a 100644
--- a/queue.go
+++ b/queue.go
@@ -11,7 +11,7 @@ type Queue[T, R any] interface {
 	IExternalQueue[T, R]
 	// Add adds a new Job to the queue and returns a EnqueuedJob to handle the job.
 	// Time complexity: O(1)
-	Add(data T, configs ...JobConfigFunc) EnqueuedJob[R]
+	Add(data T, configs ...JobConfigFunc) (EnqueuedJob[R], bool)
 	// AddAll adds multiple Jobs to the queue and returns a EnqueuedGroupJob to handle the job.
 	// Time complexity: O(n) where n is the number of Jobs added
 	AddAll(data []Item[T]) EnqueuedGroupJob[R]
@@ -20,8 +20,9 @@ type Queue[T, R any] interface {
 // Item represents a data item to be processed by a worker
 // It combines a unique identifier with a value of any generic type
 type Item[T any] struct {
-	ID    string
-	Value T
+	ID       string
+	Value    T
+	Priority int
 }
 
 // newQueue creates a new queue with the given worker and internal queue implementation
@@ -35,22 +36,30 @@ func newQueue[T, R any](worker *worker[T, R], q IQueue) *queue[T, R] {
 	}
 }
 
-func (q *queue[T, R]) Add(data T, configs ...JobConfigFunc) EnqueuedJob[R] {
+func (q *queue[T, R]) Add(data T, configs ...JobConfigFunc) (EnqueuedJob[R], bool) {
 	j := newJob[T, R](data, loadJobConfigs(q.configs, configs...))
 
-	q.internalQueue.Enqueue(j)
+	if ok := q.internalQueue.Enqueue(j); !ok {
+		j.close()
+		return nil, false
+	}
+
 	q.postEnqueue(j)
 
-	return j
+	return j, true
 }
 
 func (q *queue[T, R]) AddAll(items []Item[T]) EnqueuedGroupJob[R] {
 	l := len(items)
-	groupJob := newGroupJob[T, R](uint32(l))
+	groupJob := newGroupJob[T, R](l)
 
 	for _, item := range items {
 		j := groupJob.NewJob(item.Value, loadJobConfigs(q.configs, WithJobId(item.ID)))
-		q.internalQueue.Enqueue(j)
+		if ok := q.internalQueue.Enqueue(j); !ok {
+			j.close()
+			continue
+		}
+
 		q.postEnqueue(j)
 	}
 
diff --git a/queue_test.go b/queue_test.go
new file mode 100644
index 0000000..bbf7fa6
--- /dev/null
+++ b/queue_test.go
@@ -0,0 +1,111 @@
+package varmq
+
+import (
+	"strconv"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/goptics/varmq/internal/collections"
+)
+
+func initQueue() (*queue[string, int], *worker[string, int], *collections.Queue[any]) {
+	// Create a worker with a simple process function that doubles an integer
+	var workerFunc WorkerFunc[string, int]
+
+	workerFunc = func(data string) (int, error) {
+		// Simple processor that converts string to int and doubles it
+		val, err := strconv.Atoi(data)
+		if err != nil {
+			return 0, err
+		}
+		return val * 2, nil
+	}
+
+	// Create an internal queue from collections and bind the worker to it
+	internalQueue := collections.NewQueue[any]()
+	worker := newWorker[string, int](workerFunc)
+	queue := newQueue(worker, internalQueue)
+
+	return queue, worker, internalQueue
+}
+
+// TestQueueAdd tests the Add method of the queue
+func TestQueue(t *testing.T) {
+	t.Run("Start worker", func(t *testing.T) {
+		_, worker, _ := initQueue()
+		err := worker.start()
+		assert.Nil(t, err, "Worker should start successfully")
+	})
+
+	t.Run("Adding job to queue", func(t *testing.T) {
+		queue, _, internalQueue := initQueue()
+
+		// Test adding a job
+		job, ok := queue.Add("42")
+		assert.True(t, ok, "Job should be added successfully")
+		assert.NotNil(t, job, "Job should not be nil")
+		assert.Equal(t, 1, queue.PendingCount(), "Queue should have one pending job")
+		assert.Equal(t, 1, internalQueue.Len(), "Internal Queue should have one item")
+	})
+
+	t.Run("Processing job", func(t *testing.T) {
+		queue, worker, internalQueue := initQueue()
+
+		err := worker.start()
+		assert.Nil(t, err, "Worker should start successfully")
+
+		job, _ := queue.Add("42")
+		job.Result()
+		assert.Equal(t, 0, queue.PendingCount(), "Queue should have no pending jobs")
+		assert.Equal(t, 0, internalQueue.Len(), "Internal Queue should be empty")
+	})
+
+	t.Run("Adding multiple jobs to queue", func(t *testing.T) {
+		jobs := []Item[string]{
+			{Value: "1", ID: "job1"},
+			{Value: "2", ID: "job2"},
+			{Value: "3", ID: "job3"},
+			{Value: "4", ID: "job4"},
+			{Value: "5", ID: "job5"},
+		}
+
+		queue, worker, internalQueue := initQueue()
+
+		groupJob := queue.AddAll(jobs)
+		pending := queue.PendingCount()
+		assert.Equal(t, 5, pending, "Queue should have five pending jobs")
+		assert.Equal(t, pending, internalQueue.Len(), "Internal Queue should have five items")
+		assert.Equal(t, pending, groupJob.Len(), "Internal Queue should have five items")
+
+		err := worker.start()
+		assert.Nil(t, err, "Worker should start successfully")
+
+		results, _ := groupJob.Results()
+		_, err = groupJob.Results()
+		assert.NotNil(t, err, "Results channel should not be accessible more than once")
+
+		// Collect all results
+		var got []int
+		for r := range results {
+			got = append(got, r.Data)
+		}
+		// Expect one result per job
+		assert.Len(t, got, len(jobs))
+		// Build expected set of doubled values
+		expected := make(map[int]struct{}, len(jobs))
+		for _, j := range jobs {
+			v, _ := strconv.Atoi(j.Value)
+			expected[v*2] = struct{}{}
+		}
+		// Verify each output is one of the expected doubles
+		for _, val := range got {
+			_, ok := expected[val]
+			assert.True(t, ok, "unexpected result value: %d", val)
+		}
+
+		assert.Equal(t, 0, queue.PendingCount(), "Queue should have no pending jobs")
+		assert.Equal(t, 0, internalQueue.Len(), "Internal Queue should be empty")
+		assert.Equal(t, 0, groupJob.Len(), "Group job should have no pending jobs")
+	})
+}
diff --git a/result.go b/result.go
index a52d872..7321c3d 100644
--- a/result.go
+++ b/result.go
@@ -17,6 +17,8 @@ type EnqueuedJob[R any] interface {
 }
 
 type EnqueuedGroupJob[T any] interface {
+	// Len returns the number of jobs in the group.
+	Len() int
 	// Drain discards the job's result and error values asynchronously.
 	Drain() error
 	// Results returns a channel that will receive the results of the group
diff --git a/result_channel.go b/result_channel.go
index a6a96e9..362a3fd 100644
--- a/result_channel.go
+++ b/result_channel.go
@@ -14,7 +14,7 @@ type resultChannel[R any] struct {
 }
 
 // newResultChannel creates a new resultChannel with the specified buffer size.
-func newResultChannel[R any](cap uint32) *resultChannel[R] {
+func newResultChannel[R any](cap int) *resultChannel[R] {
 	return &resultChannel[R]{
 		ch: make(chan Result[R], cap),
 	}
diff --git a/worker.go b/worker.go
index 2807bbf..d10d78b 100644
--- a/worker.go
+++ b/worker.go
@@ -323,10 +323,6 @@ func (w *worker[T, R]) start() error {
 		return errRunningWorker
 	}
 
-	if w.Queue != getNullQueue() {
-		panic(errWorkerAlreadyBound)
-	}
-
 	// if cache is been set and cleanup interval is not set, use default cleanup interval for 10 minutes
 	if !w.isNullCache() && w.configs.CleanupCacheInterval == 0 {
 		w.configs.CleanupCacheInterval = 10 * time.Minute


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 5**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>persistent_priority.go (1)</summary><blockquote>

`18-33`: **Silent JSON marshaling error**  

Same concern as in `persistent.go`: ignore of `Json()` error may propagate corrupt payloads.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 18-18: persistent_priority.go#L18
Added line #L18 was not covered by tests

---

[warning] 27-27: persistent_priority.go#L27
Added line #L27 was not covered by tests

---

[warning] 32-32: persistent_priority.go#L32
Added line #L32 was not covered by tests

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (8)</summary><blockquote>

<details>
<summary>bench_test.go (6)</summary><blockquote>

`27-29`: **Properly handling success/failure from queue Add operation**

The benchmark now correctly handles the new method signature that includes an explicit success flag. This ensures that the benchmark only measures the performance of successful job additions.

However, the error return value from `job.Result()` is not being checked.


Consider checking the error returned by `job.Result()` for completeness:

```diff
-			if job, ok := q.Add(j); ok {
-				job.Result()
-			}
+			if job, ok := q.Add(j); ok {
+				_, err := job.Result()
+				// In a benchmark we can ignore the error, but in production code
+				// you would want to handle this error appropriately
+			}
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 golangci-lint (1.64.8)</summary>

28-28: Error return value of `job.Result` is not checked

(errcheck)

</details>

</details>

---

`67-69`: **Error handling improvement needed in parallel benchmark**

The same issue with not checking the return value from `job.Result()` exists in the parallel benchmark.

Consider checking the error returned by `job.Result()` for completeness:

```diff
-				if job, ok := q.Add(1); ok {
-					job.Result()
-				}
+				if job, ok := q.Add(1); ok {
+					_, err := job.Result()
+					// In a benchmark we can ignore the error, but in production code
+					// you would want to handle this error appropriately
+				}
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 golangci-lint (1.64.8)</summary>

68-68: Error return value of `job.Result` is not checked

(errcheck)

</details>

</details>

---

`109-111`: **Consistent error handling improvement needed in priority queue benchmark**

The error handling pattern in the priority queue benchmark should also be improved for consistency.

Consider checking the error returned by `job.Result()` for completeness:

```diff
-			if job, ok := q.Add(i, i%10); ok {
-				job.Result()
-			}
+			if job, ok := q.Add(i, i%10); ok {
+				_, err := job.Result()
+				// In a benchmark we can ignore the error, but in production code
+				// you would want to handle this error appropriately
+			}
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 golangci-lint (1.64.8)</summary>

110-110: Error return value of `job.Result` is not checked

(errcheck)

</details>

</details>

---

`149-151`: **Error handling improvement needed in parallel priority queue benchmark**

The pattern of not checking the error from `job.Result()` is repeated here as well.

Consider checking the error returned by `job.Result()` for consistency:

```diff
-				if job, ok := q.Add(1, 0); ok {
-					job.Result()
-				}
+				if job, ok := q.Add(1, 0); ok {
+					_, err := job.Result()
+					// In a benchmark we can ignore the error, but in production code
+					// you would want to handle this error appropriately
+				}
```

---

`192-194`: **Error handling improvement needed in void worker benchmark**

The same pattern of not checking errors from `job.Result()` is present in the void worker benchmark.

Consider checking the error returned by `job.Result()` for consistency:

```diff
-			if job, ok := q.Add(j); ok {
-				job.Result()
-			}
+			if job, ok := q.Add(j); ok {
+				_, err := job.Result()
+				// In a benchmark we can ignore the error, but in production code
+				// you would want to handle this error appropriately
+			}
```

---

`233-235`: **Consistent error handling improvement needed in parallel void worker benchmark**

The code shows the same pattern of not checking the error return value from `job.Result()`.

Consider checking the error returned by `job.Result()` for consistency:

```diff
-				if job, ok := q.Add(1); ok {
-					job.Result()
-				}
+				if job, ok := q.Add(1); ok {
+					_, err := job.Result()
+					// In a benchmark we can ignore the error, but in production code
+					// you would want to handle this error appropriately
+				}
```

</blockquote></details>
<details>
<summary>queue.go (1)</summary><blockquote>

`23-26`: **`Priority` field is unused for the plain queue**  

`Item` now carries a `Priority`, but `queue.AddAll` (plain, non-priority) ignores it and the underlying `IQueue.Enqueue` lacks a priority parameter.  
Consider:

* Documenting that `Priority` is ignored for non-priority queues (to avoid user confusion), **or**
* Splitting the struct into `Item` and `PriorityItem` to keep the API explicit.

</blockquote></details>
<details>
<summary>group_job.go (1)</summary><blockquote>

`98-102`: **`Add(^uint32(0))` is clever but opaque**  

Using the bit-wise complement to subtract one is correct, yet cryptic for most readers.  
Consider the clearer `Add(^uint32(0)) /* -1 */` comment or simply:

```go
gj.jobCount.Add(^uint32(0)) // decrement by 1
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 golangci-lint (1.64.8)</summary>

99-99: Error return value of `gj.Ack` is not checked

(errcheck)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between fd6b113a7debc88c59b51e885b014a03abc731ff and 7be61b42c4959077928c8e075ee73216ae6da767.

</details>

<details>
<summary>📒 Files selected for processing (9)</summary>

* `bench_test.go` (9 hunks)
* `group_job.go` (3 hunks)
* `group_job_test.go` (4 hunks)
* `persistent.go` (2 hunks)
* `persistent_priority.go` (2 hunks)
* `priority.go` (2 hunks)
* `queue.go` (3 hunks)
* `result.go` (1 hunks)
* `result_channel.go` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (5)</summary>

<details>
<summary>persistent.go (3)</summary><blockquote>

<details>
<summary>job_config.go (1)</summary>

* `JobConfigFunc` (3-3)

</details>
<details>
<summary>result.go (2)</summary>

* `EnqueuedJob` (11-17)
* `EnqueuedGroupJob` (19-24)

</details>
<details>
<summary>queue.go (1)</summary>

* `Item` (22-26)

</details>

</blockquote></details>
<details>
<summary>bench_test.go (2)</summary><blockquote>

<details>
<summary>result.go (1)</summary>

* `Result` (4-8)

</details>
<details>
<summary>queue.go (1)</summary>

* `Item` (22-26)

</details>

</blockquote></details>
<details>
<summary>persistent_priority.go (3)</summary><blockquote>

<details>
<summary>job_config.go (1)</summary>

* `JobConfigFunc` (3-3)

</details>
<details>
<summary>result.go (2)</summary>

* `EnqueuedJob` (11-17)
* `EnqueuedGroupJob` (19-24)

</details>
<details>
<summary>queue.go (1)</summary>

* `Item` (22-26)

</details>

</blockquote></details>
<details>
<summary>queue.go (2)</summary><blockquote>

<details>
<summary>job_config.go (2)</summary>

* `JobConfigFunc` (3-3)
* `WithJobId` (21-28)

</details>
<details>
<summary>result.go (2)</summary>

* `EnqueuedJob` (11-17)
* `EnqueuedGroupJob` (19-24)

</details>

</blockquote></details>
<details>
<summary>priority.go (3)</summary><blockquote>

<details>
<summary>job_config.go (2)</summary>

* `JobConfigFunc` (3-3)
* `WithJobId` (21-28)

</details>
<details>
<summary>result.go (2)</summary>

* `EnqueuedJob` (11-17)
* `EnqueuedGroupJob` (19-24)

</details>
<details>
<summary>queue.go (1)</summary>

* `Item` (22-26)

</details>

</blockquote></details>

</details><details>
<summary>🪛 GitHub Check: codecov/patch</summary>

<details>
<summary>persistent.go</summary>

[warning] 27-27: persistent.go#L27
Added line #L27 was not covered by tests

---

[warning] 36-36: persistent.go#L36
Added line #L36 was not covered by tests

---

[warning] 41-41: persistent.go#L41
Added line #L41 was not covered by tests

---

[warning] 49-50: persistent.go#L49-L50
Added lines #L49 - L50 were not covered by tests

</details>
<details>
<summary>persistent_priority.go</summary>

[warning] 18-18: persistent_priority.go#L18
Added line #L18 was not covered by tests

---

[warning] 27-27: persistent_priority.go#L27
Added line #L27 was not covered by tests

---

[warning] 32-32: persistent_priority.go#L32
Added line #L32 was not covered by tests

---

[warning] 35-36: persistent_priority.go#L35-L36
Added lines #L35 - L36 were not covered by tests

</details>
<details>
<summary>group_job.go</summary>

[warning] 65-66: group_job.go#L65-L66
Added lines #L65 - L66 were not covered by tests

</details>
<details>
<summary>queue.go</summary>

[warning] 39-39: queue.go#L39
Added line #L39 was not covered by tests

---

[warning] 42-45: queue.go#L42-L45
Added lines #L42 - L45 were not covered by tests

---

[warning] 47-47: queue.go#L47
Added line #L47 was not covered by tests

---

[warning] 52-52: queue.go#L52
Added line #L52 was not covered by tests

---

[warning] 56-58: queue.go#L56-L58
Added lines #L56 - L58 were not covered by tests

</details>
<details>
<summary>priority.go</summary>

[warning] 32-32: persistent_priority.go#L32
Added line #L32 was not covered by tests

---

[warning] 35-36: persistent_priority.go#L35-L36
Added lines #L35 - L36 were not covered by tests

---

[warning] 28-28: priority.go#L28
Added line #L28 was not covered by tests

---

[warning] 31-34: priority.go#L31-L34
Added lines #L31 - L34 were not covered by tests

---

[warning] 38-38: priority.go#L38
Added line #L38 was not covered by tests

---

[warning] 41-41: priority.go#L41
Added line #L41 was not covered by tests

---

[warning] 43-43: priority.go#L43
Added line #L43 was not covered by tests

---

[warning] 48-50: priority.go#L48-L50
Added lines #L48 - L50 were not covered by tests

</details>

</details>
<details>
<summary>🪛 golangci-lint (1.64.8)</summary>

<details>
<summary>bench_test.go</summary>

28-28: Error return value of `job.Result` is not checked

(errcheck)

---

68-68: Error return value of `job.Result` is not checked

(errcheck)

---

110-110: Error return value of `job.Result` is not checked

(errcheck)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (18)</summary><blockquote>

<details>
<summary>result.go (1)</summary>

`28-29`: **Excellent addition to the EnqueuedSingleGroupJob interface**

Adding the `JobCount()` method to the interface provides a clear way to access the number of jobs in a group without exposing internal implementation details. This aligns well with the broader changes to improve job management and tracking.

</details>
<details>
<summary>result_channel.go (1)</summary>

`17-17`: **Consistent type usage for buffer sizes**

Changing the parameter type from `uint32` to `int` for the buffer size enhances consistency across the codebase. This change aligns with similar modifications in related components, creating a more uniform API for specifying buffer capacities.

</details>
<details>
<summary>group_job_test.go (5)</summary>

`13-14`: **Appropriate type update in test**

The change from `uint32` to `int` for `bufferSize` aligns with the updated signature of `newGroupJob` in the implementation, maintaining consistency between code and tests.

---

`39-40`: **Type consistency maintained**

Converting `bufferSize` to `int` here maintains consistency with the implementation changes.

---

`59-60`: **Type alignment for bufferSize**

Changing to `int` type here correctly aligns with the updated function signature.

---

`64-64`: **Cleaner arithmetic operation**

The code now directly uses the integer type without explicit casting, resulting in cleaner code that's easier to read.

---

`82-83`: **Consistent type usage**

This change ensures the test code uses the same types as the implementation code, maintaining consistency throughout the codebase.

</details>
<details>
<summary>bench_test.go (4)</summary>

`122-125`: **Updated data structure usage for priority queue items**

The code now correctly uses the unified `Item` type with a `Priority` field instead of the previous `PQItem` type. This reflects the simplification of the item structures in the main implementation.

---

`163-166`: **Consistent data structure usage in parallel benchmark**

The code correctly uses the same unified `Item` type with `Priority` field in the parallel benchmark, maintaining consistency across the codebase.

---

`171-175`: **Improved error handling for batch operations**

The code now properly checks for errors when calling `Results()` before attempting to drain the channel. This is an important improvement that prevents potential panics if the operation fails.

---

`212-216`: **Proper error handling in batch operations for void worker**

The code correctly checks for errors from `Results()` before accessing the channel, preventing potential panics if the operation fails.

</details>
<details>
<summary>queue.go (1)</summary>

`56-60`: **Good defensive programming on batch enqueue**  

Closing and skipping failed jobs keeps group semantics consistent.  
👍

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 56-58: queue.go#L56-L58
Added lines #L56 - L58 were not covered by tests

</details>

</details>

</details>
<details>
<summary>persistent.go (1)</summary>

`49-51`: **Nice switch from `uint32` to `int` for buffer size**  

Keeps the API idiomatic and avoids unnecessary casting.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 49-50: persistent.go#L49-L50
Added lines #L49 - L50 were not covered by tests

</details>

</details>

</details>
<details>
<summary>persistent_priority.go (1)</summary>

`35-53`: **Batch enqueue failure handling looks solid**  

Failed items are closed and skipped, successful ones proceed – consistent with single-enqueue semantics.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 35-36: persistent_priority.go#L35-L36
Added lines #L35 - L36 were not covered by tests

</details>

</details>

</details>
<details>
<summary>priority.go (4)</summary>

`12-12`: **Add method signature now returns a success/failure indicator.**

The signature change to return a boolean alongside the job improves error handling by explicitly signaling whether enqueuing was successful, allowing consumers to handle failures appropriately.

---

`15-15`: **Unified type system with common Item type.**

The parameter type change from `[]PQItem[T]` to `[]Item[T]` standardizes the data structure across the codebase, improving consistency and maintainability.

---

`28-28`: **Implementation correctly matches interface changes.**

The method implementation now properly returns a tuple with the job and a success indicator, which aligns with the interface definition.



Also applies to: 38-38

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 28-28: priority.go#L28
Added line #L28 was not covered by tests

</details>

</details>

---

`48-51`: <details>
<summary>❓ Verification inconclusive</summary>

**Graceful handling of per-item enqueue failures.**

The implementation now properly handles individual job enqueue failures by closing the job and continuing to the next item, which prevents resource leaks and allows partial success of batch operations.


This error handling logic is reported as untested. Consider adding tests that verify partial batch failures:

---

🏁 Script executed:

```shell
#!/bin/bash
# Check for any tests that might verify partial failures in AddAll
rg -A 15 -B 5 "Test.*AddAll.*fail" --type go
```

Length of output: 46

---

🏁 Script executed:

```shell
#!/bin/bash
# Search for any TestAddAll functions in Go test files
rg -A5 -B2 'func Test.*AddAll' --type go

# Search for any references to AddAll in test files
rg -A5 -B2 'AddAll(' --type go -g '*_test.go'
```

Length of output: 155

---



</details>

**Add tests for partial‐failure handling in AddAll**

I wasn’t able to find any existing Go tests that simulate an `Enqueue` failure within `AddAll` and verify that failed jobs are closed while subsequent items still succeed. Please add tests covering at least:

- A single‐item enqueue failure: mock or stub `internalQueue.Enqueue` to return `false` for one job, then assert that `j.close()` was called and the next items were enqueued.
- Multiple‐item failure scenarios (e.g. failures at the start, middle, and end of a batch).

This will ensure your graceful‐degradation logic in `priority.go` is exercised and guarded against regressions.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 48-50: priority.go#L48-L50
Added lines #L48 - L50 were not covered by tests

</details>

</details>

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-19T10:08:20Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>group_job.go (2)</summary><blockquote>

`12-14`: **Avoid shadowing the built-in `len` identifier**

Using the field name `len` overshadows Go’s ubiquitous built-in `len()` function and can be confusing in larger files.  
Consider renaming it to something like `counter` or `size`.

```diff
-	len *groupLen
+	counter *groupLen
```

---

`16-18`: **Expose clearer semantics in `groupLen`**

A tiny struct holding a single atomic makes the code harder to read.  If you keep it, give it a descriptive name (e.g. `jobCounter`) and document its purpose.  Alternatively, embed `atomic.Uint32` directly in `groupJob`.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 3665f25787cec3e3320dce3d0ad0adeac7173dce and 0c975fc4c85a9afe6aad838c0f460c85c70d7190.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `group_job.go` (4 hunks)
* `result.go` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* result.go

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

<details>
<summary>group_job.go</summary>

[warning] 28-29: group_job.go#L28-L29
Added lines #L28 - L29 were not covered by tests

---

[warning] 83-84: group_job.go#L83-L84
Added lines #L83 - L84 were not covered by tests

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>group_job.go (1)</summary>

`83-85`: **`Len()` accuracy depends on the two issues above**

Because of the missing root initialisation and the incorrect `Sub()`, the returned value is currently unreliable. Once those are fixed, consider adding unit tests that:

1. Create a group job,
2. Spawn `n` sub-jobs,
3. Close all jobs,
4. Assert that `Len()` eventually reaches 0.

This will also raise coverage for the lines flagged by Codecov.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 83-84: group_job.go#L83-L84
Added lines #L83 - L84 were not covered by tests

</details>

</details>

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-19T10:45:29Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>group_job.go (1)</summary><blockquote>

`34-44`: _🛠️ Refactor suggestion_

**Root job is still not counted in `Len()`.**

While you've added atomic job counting, the root job itself is still not included in the count. The `newGroupJob` function initializes the waitgroup with `bufferSize` but doesn't initialize the counter to include the root job.


```diff
 	gj.wg.Add(bufferSize)
+	// Count the root job itself so Len() starts at 1
+	gj.len.Add()
 	return gj
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>group_job.go (1)</summary><blockquote>

`16-30`: **Consider adding a comment explaining the purpose of `groupLen`.**

The new `groupLen` struct is a key part of tracking active jobs but lacks documentation explaining its purpose and how it relates to the waitgroup. Adding a comment would improve maintainability.


```diff
+// groupLen provides atomic counting operations for tracking the number of active jobs.
 type groupLen struct {
 	len atomic.Uint32
 }
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 28-29: group_job.go#L28-L29
Added lines #L28 - L29 were not covered by tests

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 0c975fc4c85a9afe6aad838c0f460c85c70d7190 and a516df53196e5a873dc2f8fd147674db4ddd1eff.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `group_job.go` (4 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

<details>
<summary>group_job.go</summary>

[warning] 28-29: group_job.go#L28-L29
Added lines #L28 - L29 were not covered by tests

---

[warning] 83-84: group_job.go#L83-L84
Added lines #L83 - L84 were not covered by tests

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (2)</summary><blockquote>

<details>
<summary>group_job.go (2)</summary>

`24-26`: **Implementation of `Sub()` properly decrements the counter.**

The bitwise complement approach (`Add(^uint32(0))`) correctly decrements the counter by 1. This is a common pattern for atomic decrement and addresses the previous issue where `Sub()` was incorrectly incrementing the counter.

---

`9-14`: **Effective implementation of shared counter.**

The change to add a shared `len` field properly addresses the previous issue where job counts weren't shared between sub-jobs. Now all sub-jobs share the same counter, allowing proper tracking of the total job count.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-19T11:39:25Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (4)</summary><blockquote>

<details>
<summary>queue_test.go (4)</summary><blockquote>

`14-15`: **Simplify variable declaration and assignment.**

You can merge the variable declaration with its assignment for better readability.

```diff
-var workerFunc WorkerFunc[string, int]
-
-workerFunc = func(data string) (int, error) {
+workerFunc := func(data string) (int, error) {
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 golangci-lint (1.64.8)</summary>

14-14: S1021: should merge variable declaration with assignment on next line

(gosimple)

</details>

</details>

---

`61-62`: **Verify the actual result value.**

The test checks that queues are emptied after processing but doesn't verify the actual result value. Consider checking that the result is as expected (84 in this case).

```diff
-		job, _ := queue.Add("42")
-		job.Result()
+		job, ok := queue.Add("42")
+		assert.True(t, ok, "Job should be added successfully")
+		result, err := job.Result()
+		assert.Nil(t, err, "Result should not have an error")
+		assert.Equal(t, 84, result, "Result should be double the input value (42*2)")
```

---

`82-84`: **Fix assertion messages for clarity.**

The assertion messages refer to "three pending jobs" or "three items" but the test is actually using 5 jobs. Update the messages for consistency.

```diff
-		assert.Equal(t, 5, queue.PendingCount(), "Queue should have three pending jobs")
-		assert.Equal(t, 5, internalQueue.Len(), "Internal Queue should have three items")
-		assert.Equal(t, 5, groupJob.Len(), "Internal Queue should have three items")
+		assert.Equal(t, 5, queue.PendingCount(), "Queue should have five pending jobs")
+		assert.Equal(t, 5, internalQueue.Len(), "Internal Queue should have five items")
+		assert.Equal(t, 5, groupJob.Len(), "Group job should have five items")
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[error] 83-84: TestQueue/Adding_multiple_jobs_to_queue failed: expected 5 but got 4 at line 83; expected 5 but got 2 at line 84. Internal Queue should have three items.

</details>

</details>

---

`95-95`: **Fix assertion message for clarity.**

The assertion message refers to "three items" but the test is actually using 5 jobs.

```diff
-		assert.Equal(t, 5, count, "Results should have three items")
+		assert.Equal(t, 5, count, "Results should have five items")
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between a516df53196e5a873dc2f8fd147674db4ddd1eff and 2aaae96eea0a9d01c4606011c2b91061fbbaf60b.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `queue_test.go` (1 hunks)
* `result.go` (1 hunks)
* `worker.go` (0 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (1)</summary>

* worker.go

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* result.go

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 golangci-lint (1.64.8)</summary>

<details>
<summary>queue_test.go</summary>

14-14: S1021: should merge variable declaration with assignment on next line

(gosimple)

</details>

</details>
<details>
<summary>🪛 GitHub Actions: CI</summary>

<details>
<summary>queue_test.go</summary>

[error] 83-84: TestQueue/Adding_multiple_jobs_to_queue failed: expected 5 but got 4 at line 83; expected 5 but got 2 at line 84. Internal Queue should have three items.

</details>

</details>

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-19T12:33:17Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>♻️ Duplicate comments (2)</summary><blockquote>

<details>
<summary>queue_test.go (2)</summary><blockquote>

`75-78`: **Handle potential enqueue failures**

According to past review comments, not all jobs might be successfully enqueued. The test should dynamically compute the number of jobs added rather than assuming all five were added successfully.


```diff
	groupJob := queue.AddAll(jobs)
-	assert.Equal(t, 5, queue.PendingCount(), "Queue should have five pending jobs")
-	assert.Equal(t, 5, internalQueue.Len(), "Internal Queue should have five items")
-	assert.Equal(t, 5, groupJob.Len(), "Internal Queue should have five items")
+	// Only successfully enqueued jobs are counted
+	addedCount := queue.PendingCount()
+	assert.Equal(t, addedCount, internalQueue.Len(), "Queue and internal queue counts should match")
+	assert.Equal(t, addedCount, groupJob.Len(), "Queue and group job counts should match")
```

---

`83-92`: **Verify result values, not just count**

The test only counts the number of results but doesn't verify their correctness.


```diff
-	results, _ := groupJob.Results()
-	_, err = groupJob.Results()
-	assert.NotNil(t, err, "Results should not be nil")
-
-	count := 0
-	for range results {
-		count++
-	}
+	results, _ := groupJob.Results()
+	_, err = groupJob.Results()
+	assert.NotNil(t, err)
+
+	// Collect all results
+	var got []int
+	for r := range results {
+		got = append(got, r)
+	}
+	// Expect one result per job
+	assert.Len(t, got, len(jobs))
+
+	// Build expected set of doubled values
+	expected := make(map[int]struct{}, len(jobs))
+	for _, j := range jobs {
+		v, _ := strconv.Atoi(j.Value)
+		expected[v*2] = struct{}{}
+	}
+
+	// Verify each output is one of the expected doubles
+	for _, val := range got {
+		_, ok := expected[val]
+		assert.True(t, ok, "unexpected result value: %d", val)
+	}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (4)</summary><blockquote>

<details>
<summary>queue_test.go (4)</summary><blockquote>

`14-23`: **Simplify variable declaration and assignment**

The variable declaration and assignment can be combined into a single line for a more concise and idiomatic Go code.


```diff
-	var workerFunc WorkerFunc[string, int]
-
-	workerFunc = func(data string) (int, error) {
+	workerFunc := func(data string) (int, error) {
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 golangci-lint (1.64.8)</summary>

14-14: S1021: should merge variable declaration with assignment on next line

(gosimple)

</details>

</details>

---

`41-50`: **Verify job data integrity**

The test checks that a job is added successfully but doesn't verify that the job contains the correct data.


Consider adding an assertion to verify that the job's data matches the input:

```diff
	job, ok := queue.Add("42")
	assert.True(t, ok, "Job should be added successfully")
	assert.NotNil(t, job, "Job should not be nil")
+	// Verify job contains the correct data
+	result, err := job.Result()
+	assert.Nil(t, err, "Job should complete without error")
+	assert.Equal(t, 84, result, "Result should be double the input")
	assert.Equal(t, 1, queue.PendingCount(), "Queue should have one pending job")
	assert.Equal(t, 1, internalQueue.Len(), "Internal Queue should have one item")
```

---

`78-78`: **Fix assertion message**

The assertion message refers to "Internal Queue" but is checking the group job length.


```diff
-	assert.Equal(t, 5, groupJob.Len(), "Internal Queue should have five items")
+	assert.Equal(t, 5, groupJob.Len(), "Group job should have five items")
```

---

`33-97`: **Add more test cases for better coverage**

The current tests cover basic functionality but lack coverage for important scenarios.


Consider adding test cases for:
1. Adding a job that fails to enqueue (negative case)
2. Processing a job that encounters an error in the worker function (e.g., invalid input like non-numeric string)
3. Edge cases like empty input to AddAll
4. Concurrent access to the queue (add jobs from multiple goroutines)

Example for testing error handling:
```go
t.Run("Processing job with error", func(t *testing.T) {
    queue, worker, _ := initQueue()
    
    err := worker.start()
    assert.Nil(t, err, "Worker should start successfully")
    
    // Add job with invalid input (non-numeric string)
    job, ok := queue.Add("not-a-number")
    assert.True(t, ok, "Job should be added successfully")
    
    // Job should complete with error
    result, err := job.Result()
    assert.NotNil(t, err, "Job should complete with error")
    assert.Equal(t, 0, result, "Result should be zero for failed job")
})
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 2aaae96eea0a9d01c4606011c2b91061fbbaf60b and 7c8f3e2d0b5d1e6e179dc3bcaa81a99f5cbf1637.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `queue_test.go` (1 hunks)
* `result.go` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* result.go

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 golangci-lint (1.64.8)</summary>

<details>
<summary>queue_test.go</summary>

14-14: S1021: should merge variable declaration with assignment on next line

(gosimple)

</details>

</details>

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-19T12:36:17Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>♻️ Duplicate comments (3)</summary><blockquote>

<details>
<summary>queue_test.go (3)</summary><blockquote>

`58-60`: **Check the return values and validate result correctness**

The test ignores the boolean return value from `queue.Add` and doesn't verify the correctness of the job result.


```diff
-	job, _ := queue.Add("42")
-	job.Result()
+	job, ok := queue.Add("42")
+	assert.True(t, ok, "Job should be added successfully")
+	result, err := job.Result()
+	assert.Nil(t, err, "Job should complete without error")
+	assert.Equal(t, 84, result, "Result should be double the input value")
```

---

`75-78`: **Update queue_test.go to handle skipped enqueues in AddAll**

The `AddAll` method now skips any jobs that fail to enqueue. The test should account for this behavior by using the actual count of successfully enqueued jobs instead of hardcoding "5".


```diff
 	groupJob := queue.AddAll(jobs)
-	assert.Equal(t, 5, queue.PendingCount(), "Queue should have five pending jobs")
-	assert.Equal(t, 5, internalQueue.Len(), "Internal Queue should have five items")
-	assert.Equal(t, 5, groupJob.Len(), "Internal Queue should have five items")
+	
+	// Only successfully enqueued jobs are counted
+	addedCount := queue.PendingCount()
+	assert.Equal(t, addedCount, internalQueue.Len(), "Queue and internal queue counts should match")
+	assert.Equal(t, addedCount, groupJob.Len(), "Queue and group job counts should match")
```

Also note that the assertion message on line 78 incorrectly refers to "Internal Queue" when it should refer to the group job.

---

`83-92`: **Assert each result's value and handle unordered output**

The test only counts the results without verifying their correctness. The test should collect all results and verify that each one is double its corresponding input value.


```diff
 	results, _ := groupJob.Results()
 	_, err = groupJob.Results()
-	assert.NotNil(t, err, "Results channel should not be accessible more than once")
+	assert.NotNil(t, err, "Results channel should not be accessible more than once")
 
-	count := 0
-	for range results {
-		count++
-	}
+	// Collect all results
+	var got []int
+	for r := range results {
+		got = append(got, r)
+	}
+	
+	// Build expected set of doubled values
+	expected := make(map[int]struct{}, len(jobs))
+	for _, j := range jobs {
+		v, _ := strconv.Atoi(j.Value)
+		expected[v*2] = struct{}{}
+	}
+	
+	// Verify each output is one of the expected doubles
+	for _, val := range got {
+		_, ok := expected[val]
+		assert.True(t, ok, "unexpected result value: %d", val)
+	}
```

Since the Results() method may not preserve order, this approach verifies that each result matches one of the expected values without relying on the sequence.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>queue_test.go (1)</summary><blockquote>

`14-15`: **Simplify variable declaration and assignment**

The variable declaration and assignment can be combined into a single statement for better readability.


```diff
-	var workerFunc WorkerFunc[string, int]
-
-	workerFunc = func(data string) (int, error) {
+	workerFunc := func(data string) (int, error) {
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 golangci-lint (1.64.8)</summary>

14-14: S1021: should merge variable declaration with assignment on next line

(gosimple)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**
**Cache: Disabled due to data retention organization setting**
**Knowledge Base: Disabled due to data retention organization setting**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 7c8f3e2d0b5d1e6e179dc3bcaa81a99f5cbf1637 and ef5fd1a26f2144518c6c2079c3b14b48f5cef6ea.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `queue_test.go` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>queue_test.go (4)</summary><blockquote>

<details>
<summary>queue.go (2)</summary>

* `Queue` (10-18)
* `Item` (22-26)

</details>
<details>
<summary>worker.go (1)</summary>

* `WorkerFunc` (14-14)

</details>
<details>
<summary>internal/collections/queue.go (1)</summary>

* `NewQueue` (13-19)

</details>
<details>
<summary>result.go (1)</summary>

* `Result` (4-8)

</details>

</blockquote></details>

</details><details>
<summary>🪛 golangci-lint (1.64.8)</summary>

<details>
<summary>queue_test.go</summary>

14-14: S1021: should merge variable declaration with assignment on next line

(gosimple)

</details>

</details>

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-19T12:40:07Z

Review comments:
diff_hunk: @@ -35,22 +36,28 @@
 	}
 }
 
-func (q *queue[T, R]) Add(data T, configs ...JobConfigFunc) EnqueuedJob[R] {
+func (q *queue[T, R]) Add(data T, configs ...JobConfigFunc) (EnqueuedJob[R], bool) {
 	j := newJob[T, R](data, loadJobConfigs(q.configs, configs...))
 
-	q.internalQueue.Enqueue(j)
-	q.postEnqueue(j)
+	if ok := q.internalQueue.Enqueue(j); ok {
+		q.postEnqueue(j)
+		return nil, false
+	}
 
-	return j
+	return j, true
 }
path: queue.go
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Inverted success logic breaks `Add` – jobs are always reported as failed**  

`internalQueue.Enqueue` returns `true` on success, yet the branch treats that as *failure*.  
As a result callers receive `(nil, false)` even when the job is actually en-queued, and `(job, true)` when the enqueue failed – the exact opposite of the contract.  
It also leaks an un-closed job instance on the failure path.

```diff
-func (q *queue[T, R]) Add(data T, configs ...JobConfigFunc) (EnqueuedJob[R], bool) {
-	j := newJob[T, R](data, loadJobConfigs(q.configs, configs...))
-
-	if ok := q.internalQueue.Enqueue(j); ok {
-		q.postEnqueue(j)
-		return nil, false
-	}
-
-	return j, true
+func (q *queue[T, R]) Add(data T, configs ...JobConfigFunc) (EnqueuedJob[R], bool) {
+	j := newJob[T, R](data, loadJobConfigs(q.configs, configs...))
+
+	if ok := q.internalQueue.Enqueue(j); !ok {
+		j.close()              // prevent a leak
+		return nil, false      // enqueue failed
+	}
+
+	q.postEnqueue(j)
+	return j, true            // enqueue succeeded
 }
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 39-39: queue.go#L39
Added line #L39 was not covered by tests

---

[warning] 42-45: queue.go#L42-L45
Added lines #L42 - L45 were not covered by tests

---

[warning] 47-47: queue.go#L47
Added line #L47 was not covered by tests

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In queue.go around lines 39 to 48, the Add method incorrectly treats a true
return from internalQueue.Enqueue as failure, returning (nil, false) on success
and (job, true) on failure, which inverts the intended logic. Fix this by
reversing the condition to check if Enqueue returns false (failure), then return
(nil, false). On success, call postEnqueue and return (job, true). This ensures
the method correctly reports enqueue success and avoids leaking unclosed job
instances.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-19T10:08:19Z
author_association: NONE
diff_hunk: @@ -3,26 +3,28 @@
 import (
 	"fmt"
 	"sync"
+	"sync/atomic"
 )
 
 // groupJob represents a job that can be used in a group.
 type groupJob[T, R any] struct {
 	*job[T, R]
-	wg *sync.WaitGroup
+	jobCount atomic.Uint32
+	wg       *sync.WaitGroup
 }
path: group_job.go
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**`jobCount` is not shared between sub-jobs – counter never decrements**  

`NewJob` allocates a brand-new `groupJob` (and thus a brand-new, zero-initialised `atomic.Uint32`).  
When a sub-job calls `close()`, it decrements *its own* counter, leaving the parent’s counter unchanged, so `JobCount()` on the root object never reaches 0.

Proposed fix: share a single counter pointer across the whole group.

```diff
-type groupJob[T, R any] struct {
-	*job[T, R]
-	jobCount atomic.Uint32
+type groupJob[T, R any] struct {
+	*job[T, R]
+	jobCount *atomic.Uint32   // shared
 	wg       *sync.WaitGroup
 }
```

and in constructors:

```diff
-	gj := &groupJob[T, R]{ ... }
+	cnt := &atomic.Uint32{}
+	cnt.Store(uint32(bufferSize))
+	gj := &groupJob[T, R]{
+		...
+		jobCount: cnt,
+	}
```

then:

```diff
 return &groupJob[T, R]{
 	job: &job[T, R]{ ... },
+	jobCount: gj.jobCount,   // share
 	wg: gj.wg,
 }
```




<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
type groupJob[T, R any] struct {
    *job[T, R]
-   jobCount atomic.Uint32
+   jobCount *atomic.Uint32   // shared
    wg       *sync.WaitGroup
}
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In group_job.go around lines 10 to 14, the jobCount atomic counter is not shared
among sub-jobs, causing the decrement in close() to affect only the sub-job's
own counter and not the parent's. To fix this, change jobCount to be a pointer
to an atomic.Uint32 and ensure that the same pointer is passed and shared across
all sub-jobs created by NewJob, so that decrements affect the shared counter and
JobCount() on the root job correctly reaches zero.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-19T10:08:19Z
author_association: NONE
diff_hunk: @@ -33,20 +33,21 @@
 
 	if ok := q.internalQueue.Enqueue(val); !ok {
 		j.close()
-		return j
+		return nil, false
 	}
path: persistent.go
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Silent JSON marshaling error is discarded**  

`val, _ := j.Json()` ignores a potential (and likely) marshaling error.  
If serialization fails the enqueue will receive garbage data.

```diff
-val, _ := j.Json()
+val, err := j.Json()
+if err != nil {
+    return nil, false // or bubble the error via a different API
+}
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 36-36: persistent.go#L36
Added line #L36 was not covered by tests

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In persistent.go around lines 34 to 37, the JSON marshaling error from j.Json()
is ignored, which can cause invalid data to be enqueued. Modify the code to
capture and check the error returned by j.Json(); if an error occurs, handle it
appropriately by not enqueuing and returning the error instead of nil, false.
This ensures only valid serialized data is enqueued and prevents silent
failures.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-19T10:08:19Z
author_association: NONE
diff_hunk: @@ -34,23 +25,31 @@
 	}
 }
 
-func (q *priorityQueue[T, R]) Add(data T, priority int, configs ...JobConfigFunc) EnqueuedJob[R] {
+func (q *priorityQueue[T, R]) Add(data T, priority int, configs ...JobConfigFunc) (EnqueuedJob[R], bool) {
 	j := newJob[T, R](data, loadJobConfigs(q.configs, configs...))
 
-	q.internalQueue.Enqueue(j, priority)
+	if ok := q.internalQueue.Enqueue(j, priority); !ok {
+		j.close()
+		return nil, false
+	}
+
 	q.postEnqueue(j)
 
-	return j
+	return j, true
 }
 
-func (q *priorityQueue[T, R]) AddAll(items []PQItem[T]) EnqueuedGroupJob[R] {
+func (q *priorityQueue[T, R]) AddAll(items []Item[T]) EnqueuedGroupJob[R] {
 	l := len(items)
-	groupJob := newGroupJob[T, R](uint32(l))
+	groupJob := newGroupJob[T, R](l)
path: priority.go
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Type standardization and simplified parameter passing.**

The method now uses the standard `Item` type and passes the integer length directly without type conversion, making the code more consistent with the rest of the codebase.


The static analysis reports these lines aren't covered by tests. Check for existing tests of the AddAll method:

---

🏁 Script executed:

```shell
#!/bin/bash
# Look for tests of the AddAll method
rg -A 5 "Test.*AddAll" --type go
```

Length of output: 34

---

🏁 Script executed:

```shell
#!/bin/bash
# Exhaustively search for any references to AddAll in test files
rg -n "AddAll\(" --glob '*_test.go'
```

Length of output: 436

---



</details>

**AddAll lacks dedicated unit tests**  
Benchmarks in bench_test.go exercise `AddAll`, but no `TestAddAll` functions exist. To ensure proper coverage and catch regressions, please add unit tests for `priorityQueue.AddAll`.  

• Locations to update:  
  - Create `TestAddAll` in a `_test.go` file (e.g., `priority_test.go`).  
  - Cover edge cases (empty slice, single item, multiple items).

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 41-41: priority.go#L41
Added line #L41 was not covered by tests

---

[warning] 43-43: priority.go#L43
Added line #L43 was not covered by tests

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In priority.go around lines 41 to 43, the AddAll method lacks dedicated unit
tests. To fix this, create a new test function named TestAddAll in a test file
like priority_test.go. Ensure the tests cover edge cases including passing an
empty slice, a single item, and multiple items to AddAll, verifying the expected
behavior and outputs for each case.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-19T10:08:19Z
author_association: NONE
diff_hunk: @@ -34,23 +25,31 @@
 	}
 }
 
-func (q *priorityQueue[T, R]) Add(data T, priority int, configs ...JobConfigFunc) EnqueuedJob[R] {
+func (q *priorityQueue[T, R]) Add(data T, priority int, configs ...JobConfigFunc) (EnqueuedJob[R], bool) {
 	j := newJob[T, R](data, loadJobConfigs(q.configs, configs...))
 
-	q.internalQueue.Enqueue(j, priority)
+	if ok := q.internalQueue.Enqueue(j, priority); !ok {
+		j.close()
+		return nil, false
+	}
path: priority.go
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Improved error handling for failed enqueues.**

The code now properly handles enqueue failures by closing the job and returning nil with a false indicator, preventing potential issues with using failed jobs.


The static analysis reports this error handling code is not covered by tests. Consider adding test cases that verify the behavior when enqueuing fails:

---

🏁 Script executed:

```shell
#!/bin/bash
# Check for existing tests that might verify this error handling code
rg -A 10 -B 10 "Test.*Priority.*Queue.*Add.*fail" --type go
# Or look for any mocks that could be used to simulate queue failures
rg "mock.*priority.*queue" --type go
```

Length of output: 100

---

🏁 Script executed:

```shell
#!/bin/bash
# Search for any test cases exercising Enqueue in *_test.go
rg "Enqueue" -n --glob "*_test.go"
# Search for any tests referencing internalQueue
rg "internalQueue" -n --glob "*_test.go"
```

Length of output: 1829

---



</details>

**Add tests for failed `Enqueue` branch in PriorityQueue**

The error‐handling path in `internal/collections/priority.go` (lines 31–34) where `internalQueue.Enqueue` returns `false` isn’t covered by any existing tests. Please add a test that simulates an enqueue failure and verifies that the job is closed and the method returns `nil, false`.

Key location to test:
- internal/collections/priority.go:31–34

Suggested approach:
- Expose or inject a stubbed `internalQueue` whose `Enqueue` method returns `false`.
- In your `_test.go`, construct a `PriorityQueue` using the stub, call `Enqueue`, and assert:
  - The returned job is `nil`.
  - The returned boolean is `false`.
  - Any cleanup (e.g., `job.close()`) is invoked.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 32-32: persistent_priority.go#L32
Added line #L32 was not covered by tests

---

[warning] 31-34: priority.go#L31-L34
Added lines #L31 - L34 were not covered by tests

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In priority.go around lines 31 to 34, the error handling for failed Enqueue
calls is not covered by tests. To fix this, add a test in the corresponding
_test.go file that simulates the internalQueue.Enqueue method returning false by
injecting a stub or mock internalQueue. The test should call the PriorityQueue's
Enqueue method, then assert that it returns nil and false, and verify that the
job's close method is called to ensure proper cleanup.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-19T10:08:20Z
author_association: NONE
diff_hunk: @@ -3,26 +3,44 @@
 import (
 	"fmt"
 	"sync"
+	"sync/atomic"
 )
 
 // groupJob represents a job that can be used in a group.
 type groupJob[T, R any] struct {
 	*job[T, R]
-	wg *sync.WaitGroup
+	wg  *sync.WaitGroup
+	len *groupLen
+}
+
+type groupLen struct {
+	len atomic.Uint32
+}
+
+func (gl *groupLen) Add() {
+	gl.len.Add(1)
+}
+
+func (gl *groupLen) Sub() {
+	gl.len.Add(1)
+}
path: group_job.go
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**`Sub()` increases the counter instead of decreasing it – breaks job-count invariant**

`Sub()` currently calls `Add(1)`, so every close () call *increments* the counter rather than decrementing it.  
This prevents the counter from ever reaching zero and breaks `Len()`-based logic.

```diff
 func (gl *groupLen) Sub() {
-	gl.len.Add(1)
+	// atomic.Uint32 has no dedicated Sub method; use two-complement to subtract 1
+	gl.len.Add(^uint32(0)) // decrement by 1
 }
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
func (gl *groupLen) Sub() {
	// atomic.Uint32 has no dedicated Sub method; use two’s-complement to subtract 1
	gl.len.Add(^uint32(0)) // decrement by 1
}
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In group_job.go around lines 24 to 26, the Sub() method incorrectly increments
the counter by calling Add(1) instead of decrementing it. To fix this, change
the argument in the Add() call within Sub() from 1 to -1 so that Sub() properly
decreases the counter, maintaining the job-count invariant.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit a516df5
created_at: 2025-05-19T10:45:29Z
author_association: NONE
diff_hunk: @@ -3,26 +3,44 @@
 import (
 	"fmt"
 	"sync"
+	"sync/atomic"
 )
 
 // groupJob represents a job that can be used in a group.
 type groupJob[T, R any] struct {
 	*job[T, R]
-	wg *sync.WaitGroup
+	wg  *sync.WaitGroup
+	len *groupLen
+}
+
+type groupLen struct {
+	len atomic.Uint32
+}
+
+func (gl *groupLen) Add() {
+	gl.len.Add(1)
+}
+
+func (gl *groupLen) Sub() {
+	gl.len.Add(1)
+}
+
+func (gl *groupLen) Get() int {
+	return int(gl.len.Load())
 }
 
 const groupIdPrefixed = "g:"
 
-func newGroupJob[T, R any](bufferSize uint32) *groupJob[T, R] {
+func newGroupJob[T, R any](bufferSize int) *groupJob[T, R] {
 	gj := &groupJob[T, R]{
 		job: &job[T, R]{
 			resultChannel: newResultChannel[R](bufferSize),
 		},
-		wg: new(sync.WaitGroup),
+		wg:  new(sync.WaitGroup),
+		len: new(groupLen),
 	}
 
-	gj.wg.Add(int(bufferSize))
-
+	gj.wg.Add(bufferSize)
path: group_job.go
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Root job is not counted – `Len()` never reflects the initial job**

`newGroupJob` pre-allocates the wait-group with `bufferSize` but does **not** initialise the shared counter.  
Unless callers remember to call `Add()` manually, `Len()` starts at 0 and only tracks *child* jobs, not the root.

```diff
 	len: new(groupLen),
 }
 
-	gj.wg.Add(bufferSize)
+	gj.wg.Add(bufferSize)
+
+	// Count the root job itself so Len() starts at 1
+	gj.len.Add()
```

Alternatively, store `bufferSize` if that’s the intended initial size.  
Please clarify the intended semantics and initialise accordingly.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In group_job.go around lines 34 to 43, the wait-group is initialized with
Add(bufferSize) but the shared counter for the root job is not set, causing
Len() to miss the initial job count. To fix this, clarify whether Len() should
include the root job; if yes, initialize the shared counter to bufferSize or
bufferSize plus one accordingly. Ensure the shared counter and wait-group are
consistently initialized to reflect the total number of jobs including the root.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-19T10:45:29Z
author_association: NONE
diff_hunk: @@ -60,6 +80,10 @@
 	return ch, nil
 }
 
+func (gj *groupJob[T, R]) Len() int {
+	return gj.len.Get()
+}
path: group_job.go
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Add tests for the new `Len()` method.**

The static analysis indicates this method isn't covered by tests. Please add test cases that verify the `Len()` method correctly tracks job counts, especially across job creation and completion.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if there are any tests for the Len() method
grep -r "Len()" --include="*_test.go" .
```

Length of output: 3049

---



</details>

**Add tests for `groupJob.Len()`**

No existing tests cover the new `Len()` method in `group_job.go`. Please add unit tests (e.g. in `group_job_test.go`) that verify:

- Initial length is zero when no jobs have been registered.
- Length increments correctly when jobs are created/added.
- Length decrements correctly when jobs complete or are removed.
- Edge cases (e.g., concurrent job registration and completion) if applicable.

This will ensure that `Len()` accurately reflects the job count throughout the lifecycle.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

[warning] 83-84: group_job.go#L83-L84
Added lines #L83 - L84 were not covered by tests

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In group_job.go around lines 83 to 85, the new Len() method lacks test coverage.
Add unit tests in group_job_test.go to verify Len() returns zero initially,
increments as jobs are added, decrements as jobs complete or are removed, and
handles edge cases like concurrent job registration and completion to ensure
accurate job count tracking.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-19T11:39:24Z
author_association: NONE
diff_hunk: @@ -0,0 +1,100 @@
+package varmq
+
+import (
+	"strconv"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/goptics/varmq/internal/collections"
+)
+
+func initQueue() (*queue[string, int], *worker[string, int], *collections.Queue[any]) {
+	// Create a worker with a simple process function that doubles an integer
+	var workerFunc WorkerFunc[string, int]
+
+	workerFunc = func(data string) (int, error) {
+		// Simple processor that converts string to int and doubles it
+		val, err := strconv.Atoi(data)
+		if err != nil {
+			return 0, err
+		}
+		return val * 2, nil
+	}
+
+	// Create an internal queue from collections and bind the worker to it
+	internalQueue := collections.NewQueue[any]()
+	worker := newWorker[string, int](workerFunc)
+	queue := newQueue(worker, internalQueue)
+
+	return queue, worker, internalQueue
+}
+
+// TestQueueAdd tests the Add method of the queue
+func TestQueue(t *testing.T) {
+	t.Run("Start worker", func(t *testing.T) {
+		_, worker, _ := initQueue()
+		err := worker.start()
+		assert.Nil(t, err, "Worker should start successfully")
+	})
+
+	t.Run("Adding job to queue", func(t *testing.T) {
+		queue, worker, internalQueue := initQueue()
+
+		err := worker.start()
+		assert.Nil(t, err, "Worker should start successfully")
+
+		// Test adding a job
+		job, ok := queue.Add("42")
+		assert.True(t, ok, "Job should be added successfully")
+		assert.NotNil(t, job, "Job should not be nil")
+		assert.Equal(t, 1, queue.PendingCount(), "Queue should have one pending job")
+		assert.Equal(t, 1, internalQueue.Len(), "Internal Queue should have one item")
+	})
+
+	t.Run("Processing job", func(t *testing.T) {
+		queue, worker, internalQueue := initQueue()
+
+		err := worker.start()
+		assert.Nil(t, err, "Worker should start successfully")
+
+		job, _ := queue.Add("42")
+		job.Result()
+		assert.Equal(t, 0, queue.PendingCount(), "Queue should have no pending jobs")
+		assert.Equal(t, 0, internalQueue.Len(), "Internal Queue should be empty")
+	})
+
+	t.Run("Adding multiple jobs to queue", func(t *testing.T) {
+		jobs := []Item[string]{
+			{Value: "1", ID: "job1"},
+			{Value: "2", ID: "job2"},
+			{Value: "3", ID: "job3"},
+			{Value: "4", ID: "job4"},
+			{Value: "5", ID: "job5"},
+		}
+
+		queue, worker, internalQueue := initQueue()
+
+		err := worker.start()
+		assert.Nil(t, err, "Worker should start successfully")
+
+		groupJob := queue.AddAll(jobs)
+		assert.Equal(t, 5, queue.PendingCount(), "Queue should have three pending jobs")
+		assert.Equal(t, 5, internalQueue.Len(), "Internal Queue should have three items")
+		assert.Equal(t, 5, groupJob.Len(), "Internal Queue should have three items")
+
path: queue_test.go
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Fix test failure: not all jobs are being successfully enqueued.**

According to the pipeline failure, not all jobs are being successfully added to the queue:
- Expected 5 items in internal queue but got 4
- Expected 5 items in group job but got 2

Based on the AI summary, `AddAll` now handles enqueue failures by skipping failed jobs. The test needs to be updated to account for this behavior.

```diff
 		groupJob := queue.AddAll(jobs)
-		assert.Equal(t, 5, queue.PendingCount(), "Queue should have three pending jobs")
-		assert.Equal(t, 5, internalQueue.Len(), "Internal Queue should have three items")
-		assert.Equal(t, 5, groupJob.Len(), "Internal Queue should have three items")
+		
+		// Check how many jobs were actually added successfully
+		addedCount := queue.PendingCount()
+		assert.Equal(t, addedCount, internalQueue.Len(), "Queue and Internal Queue counts should match")
+		assert.Equal(t, addedCount, groupJob.Len(), "Queue and Group job counts should match")
+		
+		// If we want to ensure all jobs are added, we need to fix what's causing the failures
+		// Alternatively, we could verify specific jobs made it into the queue
```

Additionally, please check what might be causing some jobs to fail enqueueing:
1. Is there a queue capacity limit?
2. Are there validation rules preventing some jobs from being added?
3. Is there an issue with the test setup?

---

🏁 Script executed:

```shell
#!/bin/bash
# Search for queue capacity or size limitations in the codebase
echo "Looking for queue capacity or size limitations..."
rg -A 3 "capacity|size|limit|maxSize" --type go

# Look for the AddAll implementation to understand how it handles failures
echo "Looking for AddAll implementation..."
rg -A 10 "func.*AddAll" --type go

# Look for job validation logic that might reject some jobs
echo "Looking for job validation logic..."
rg -A 5 "validate|valid|reject" --type go
```

Length of output: 18358

---

We need to inspect the group job implementation to understand how `groupJob.Len()` and pending counts are tracked. Let's locate `newGroupJob` and related methods.

---

🏁 Script executed:

```shell
#!/bin/bash
# Find the group job implementation
echo "Locating newGroupJob and EnqueuedGroupJob..."
rg -A 20 "type .*GroupJob" --type go
rg -A 20 "func newGroupJob" --type go
```

Length of output: 1444

---

🏁 Script executed:

```shell
#!/bin/bash
# Show queue.go around AddAll and struct definition
echo "Inspecting queue.go struct and AddAll method..."
rg -A20 -B10 "func (q .*AddAll" --type go queue.go
rg -A20 "type queue" --type go queue.go

# Show group_job.go: groupJob, Len, and groupLen implementation
echo "Inspecting group_job.go for groupJob, Len, and groupLen..."
rg -A30 "type groupJob" --type go group_job.go
rg -A10 "func (gj .*Len)" --type go group_job.go
rg -A20 "type groupLen" --type go group_job.go
rg -A20 "func (g \\*groupLen)" --type go group_job.go

# Show job.close in job.go to see how failed enqueues affect groupLen/wg
echo "Inspecting job.close implementation..."
rg -A10 "func (j \\*job).*close" --type go job.go
```

Length of output: 2314

---



</details>

**Update queue_test.go to handle skipped enqueues in AddAll and fix assertion messages**

The `AddAll` method now skips any jobs that fail to enqueue, so tests that hard-code a count of 5 will break when some jobs are dropped. Also, the existing messages refer to “three items” even though 5 jobs are used.

• Compute the actual number of successfully enqueued jobs  
• Assert the queue, internalQueue and groupJob lengths against that dynamic count  
• Fix the assertion messages to be accurate and consistent  

Suggested diff:
```diff
@@ queue_test.go:81-85
-		groupJob := queue.AddAll(jobs)
-		assert.Equal(t, 5, queue.PendingCount(), "Queue should have three pending jobs")
-		assert.Equal(t, 5, internalQueue.Len(), "Internal Queue should have three items")
-		assert.Equal(t, 5, groupJob.Len(), "Internal Queue should have three items")
+		groupJob := queue.AddAll(jobs)
+		// Only successfully enqueued jobs are counted
+		addedCount := queue.PendingCount()
+		assert.Equal(t, addedCount, internalQueue.Len(), "Queue and internal queue counts should match")
+		assert.Equal(t, addedCount, groupJob.Len(), "Queue and group job counts should match")
```

Additionally, investigate why some enqueues are failing (capacity limits, validation rules, etc.) and either adjust the test setup or fix the underlying issue.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[error] 83-84: TestQueue/Adding_multiple_jobs_to_queue failed: expected 5 but got 4 at line 83; expected 5 but got 2 at line 84. Internal Queue should have three items.

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In queue_test.go around lines 81 to 85, the test incorrectly assumes all 5 jobs
are enqueued, but AddAll now skips failed enqueues. Update the test to
dynamically compute the count of successfully enqueued jobs returned by AddAll,
then assert queue.PendingCount(), internalQueue.Len(), and groupJob.Len()
against this dynamic count. Also, correct the assertion messages to reflect the
actual number of jobs enqueued. Additionally, investigate potential causes for
enqueue failures such as queue capacity limits or job validation rules and
adjust the test setup or fix the root cause accordingly.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-19T12:33:16Z
author_association: NONE
diff_hunk: @@ -0,0 +1,100 @@
+package varmq
+
+import (
+	"strconv"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/goptics/varmq/internal/collections"
+)
+
+func initQueue() (*queue[string, int], *worker[string, int], *collections.Queue[any]) {
+	// Create a worker with a simple process function that doubles an integer
+	var workerFunc WorkerFunc[string, int]
+
+	workerFunc = func(data string) (int, error) {
+		// Simple processor that converts string to int and doubles it
+		val, err := strconv.Atoi(data)
+		if err != nil {
+			return 0, err
+		}
+		return val * 2, nil
+	}
+
+	// Create an internal queue from collections and bind the worker to it
+	internalQueue := collections.NewQueue[any]()
+	worker := newWorker[string, int](workerFunc)
+	queue := newQueue(worker, internalQueue)
+
+	return queue, worker, internalQueue
+}
+
+// TestQueueAdd tests the Add method of the queue
+func TestQueue(t *testing.T) {
+	t.Run("Start worker", func(t *testing.T) {
+		_, worker, _ := initQueue()
+		err := worker.start()
+		assert.Nil(t, err, "Worker should start successfully")
+	})
+
+	t.Run("Adding job to queue", func(t *testing.T) {
+		queue, worker, internalQueue := initQueue()
+
+		err := worker.start()
+		assert.Nil(t, err, "Worker should start successfully")
+
+		// Test adding a job
+		job, ok := queue.Add("42")
+		assert.True(t, ok, "Job should be added successfully")
+		assert.NotNil(t, job, "Job should not be nil")
+		assert.Equal(t, 1, queue.PendingCount(), "Queue should have one pending job")
+		assert.Equal(t, 1, internalQueue.Len(), "Internal Queue should have one item")
+	})
+
+	t.Run("Processing job", func(t *testing.T) {
+		queue, worker, internalQueue := initQueue()
+
+		err := worker.start()
+		assert.Nil(t, err, "Worker should start successfully")
+
+		job, _ := queue.Add("42")
+		job.Result()
+		assert.Equal(t, 0, queue.PendingCount(), "Queue should have no pending jobs")
+		assert.Equal(t, 0, internalQueue.Len(), "Internal Queue should be empty")
+	})
+
+	t.Run("Adding multiple jobs to queue", func(t *testing.T) {
+		jobs := []Item[string]{
+			{Value: "1", ID: "job1"},
+			{Value: "2", ID: "job2"},
+			{Value: "3", ID: "job3"},
+			{Value: "4", ID: "job4"},
+			{Value: "5", ID: "job5"},
+		}
+
+		queue, worker, internalQueue := initQueue()
+
+		err := worker.start()
+		assert.Nil(t, err, "Worker should start successfully")
+
+		groupJob := queue.AddAll(jobs)
+		assert.Equal(t, 5, queue.PendingCount(), "Queue should have three pending jobs")
+		assert.Equal(t, 5, internalQueue.Len(), "Internal Queue should have three items")
+		assert.Equal(t, 5, groupJob.Len(), "Internal Queue should have three items")
+
+		results, _ := groupJob.Results()
+		_, err = groupJob.Results()
+		assert.NotNil(t, err, "Results should not be nil")
+
+		count := 0
+		for range results {
+			count++
+		}
path: queue_test.go
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Verify the actual result values.**

The test only counts the results but doesn't verify their values. Consider checking that each result is correctly computed (double the input value).

```diff
 		results, _ := groupJob.Results()
 		_, err = groupJob.Results()
 		assert.NotNil(t, err, "Results should not be nil")
 
 		count := 0
-		for range results {
+		for i, result := range results {
+			// Check that each result is double the input value
+			expectedVal, _ := strconv.Atoi(jobs[i].Value)
+			assert.Equal(t, expectedVal*2, result, "Result should be double the input value")
 			count++
 		}
```

Note: This assumes the results are returned in the same order as the input jobs. If that's not guaranteed, you may need a different approach to verify the results.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if Results() preserves order
echo "Looking for Results implementation to check if order is preserved..."
rg -A 10 "func.*Results" --type go
```

Length of output: 600

---

🏁 Script executed:

```shell
#!/bin/bash
# Display groupJob struct definition
echo "== groupJob definition =="
rg -A 10 "type groupJob" --type go group_job.go

# Show methods writing to resultChannel in group_job.go
echo ""
echo "== resultChannel.Write invocations =="
rg -A 10 "resultChannel.Write" --type go group_job.go || echo "No direct invocations found in group_job.go"

# Find the Channel type declaration
echo ""
echo "== Channel type declaration =="
rg -A 20 "type Channel" --type go

# Show Channel.Read implementation
echo ""
echo "== Channel.Read implementation =="
rg -A 20 "func (.*Channel.*) Read" --type go

# Show Channel.Write implementation
echo ""
echo "== Channel.Write implementation =="
rg -A 20 "func (.*Channel.*) Write" --type go
```

Length of output: 1593

---

🏁 Script executed:

```shell
#!/bin/bash
# Show the full Results method implementation, including the goroutine that sends results
rg -A 200 "func (gj \\*groupJob.*Results" --type go group_job.go
```

Length of output: 150

---



</details>

**Assert each result’s value and handle unordered output**  

In queue_test.go (around lines 86–93), the test only counts results but never checks they’re actually double the inputs. Since Results() streams values as they complete (ordering isn’t guaranteed), replace the simple count with code that both collects all results and asserts each one matches an expected doubled value, regardless of order:

```diff
-   results, _ := groupJob.Results()
-   _, err = groupJob.Results()
-   assert.NotNil(t, err, "Results should not be nil")
-
-   count := 0
-   for range results {
-       count++
-   }
+   results, _ := groupJob.Results()
+   _, err = groupJob.Results()
+   assert.NotNil(t, err)
+
+   // Collect all results
+   var got []int
+   for r := range results {
+       got = append(got, r)
+   }
+   // Expect one result per job
+   assert.Len(t, got, len(jobs))
+
+   // Build expected set of doubled values
+   expected := make(map[int]struct{}, len(jobs))
+   for _, j := range jobs {
+       v, _ := strconv.Atoi(j.Value)
+       expected[v*2] = struct{}{}
+   }
+
+   // Verify each output is one of the expected doubles
+   for _, val := range got {
+       _, ok := expected[val]
+       assert.True(t, ok, "unexpected result value: %d", val)
+   }
```

This ensures you’re verifying each result’s correctness without relying on channel ordering. 

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
		results, _ := groupJob.Results()
		_, err = groupJob.Results()
		assert.NotNil(t, err)

		// Collect all results
		var got []int
		for r := range results {
			got = append(got, r)
		}
		// Expect one result per job
		assert.Len(t, got, len(jobs))

		// Build expected set of doubled values
		expected := make(map[int]struct{}, len(jobs))
		for _, j := range jobs {
			v, _ := strconv.Atoi(j.Value)
			expected[v*2] = struct{}{}
		}

		// Verify each output is one of the expected doubles
		for _, val := range got {
			_, ok := expected[val]
			assert.True(t, ok, "unexpected result value: %d", val)
		}
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In queue_test.go around lines 86 to 93, the test currently only counts the
results without verifying their correctness. Modify the test to collect all
results into a slice or map, then assert that each result is exactly double its
corresponding input value. Since the Results() method may not preserve order, do
not rely on the sequence of results; instead, match results to expected values
in an order-independent way to ensure correctness.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-19T12:33:16Z
author_association: NONE
diff_hunk: @@ -0,0 +1,97 @@
+package varmq
+
+import (
+	"strconv"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/goptics/varmq/internal/collections"
+)
+
+func initQueue() (*queue[string, int], *worker[string, int], *collections.Queue[any]) {
+	// Create a worker with a simple process function that doubles an integer
+	var workerFunc WorkerFunc[string, int]
+
+	workerFunc = func(data string) (int, error) {
+		// Simple processor that converts string to int and doubles it
+		val, err := strconv.Atoi(data)
+		if err != nil {
+			return 0, err
+		}
+		return val * 2, nil
+	}
+
+	// Create an internal queue from collections and bind the worker to it
+	internalQueue := collections.NewQueue[any]()
+	worker := newWorker[string, int](workerFunc)
+	queue := newQueue(worker, internalQueue)
+
+	return queue, worker, internalQueue
+}
+
+// TestQueueAdd tests the Add method of the queue
+func TestQueue(t *testing.T) {
+	t.Run("Start worker", func(t *testing.T) {
+		_, worker, _ := initQueue()
+		err := worker.start()
+		assert.Nil(t, err, "Worker should start successfully")
+	})
+
+	t.Run("Adding job to queue", func(t *testing.T) {
+		queue, _, internalQueue := initQueue()
+
+		// Test adding a job
+		job, ok := queue.Add("42")
+		assert.True(t, ok, "Job should be added successfully")
+		assert.NotNil(t, job, "Job should not be nil")
+		assert.Equal(t, 1, queue.PendingCount(), "Queue should have one pending job")
+		assert.Equal(t, 1, internalQueue.Len(), "Internal Queue should have one item")
+	})
+
+	t.Run("Processing job", func(t *testing.T) {
+		queue, worker, internalQueue := initQueue()
+
+		err := worker.start()
+		assert.Nil(t, err, "Worker should start successfully")
+
+		job, _ := queue.Add("42")
+		job.Result()
+		assert.Equal(t, 0, queue.PendingCount(), "Queue should have no pending jobs")
path: queue_test.go
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Check the return values and validate result correctness**

The test ignores the boolean return value from `queue.Add` and doesn't verify the correctness of the job result.


```diff
-	job, _ := queue.Add("42")
-	job.Result()
+	job, ok := queue.Add("42")
+	assert.True(t, ok, "Job should be added successfully")
+	result, err := job.Result()
+	assert.Nil(t, err, "Job should complete without error")
+	assert.Equal(t, 84, result, "Result should be double the input value")
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
		job, ok := queue.Add("42")
		assert.True(t, ok, "Job should be added successfully")
		result, err := job.Result()
		assert.Nil(t, err, "Job should complete without error")
		assert.Equal(t, 84, result, "Result should be double the input value")
		assert.Equal(t, 0, queue.PendingCount(), "Queue should have no pending jobs")
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In queue_test.go around lines 58 to 60, the test ignores the boolean return
value from queue.Add and does not verify the correctness of the job result.
Update the test to check the boolean return value to ensure the job was added
successfully, and add assertions to validate that the job.Result() returns the
expected result to confirm correct job processing.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-19T12:36:16Z
author_association: NONE
diff_hunk: @@ -0,0 +1,97 @@
+package varmq
+
+import (
+	"strconv"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/goptics/varmq/internal/collections"
+)
+
+func initQueue() (*queue[string, int], *worker[string, int], *collections.Queue[any]) {
+	// Create a worker with a simple process function that doubles an integer
+	var workerFunc WorkerFunc[string, int]
+
+	workerFunc = func(data string) (int, error) {
+		// Simple processor that converts string to int and doubles it
+		val, err := strconv.Atoi(data)
+		if err != nil {
+			return 0, err
+		}
+		return val * 2, nil
+	}
+
+	// Create an internal queue from collections and bind the worker to it
+	internalQueue := collections.NewQueue[any]()
+	worker := newWorker[string, int](workerFunc)
+	queue := newQueue(worker, internalQueue)
+
+	return queue, worker, internalQueue
+}
+
+// TestQueueAdd tests the Add method of the queue
+func TestQueue(t *testing.T) {
+	t.Run("Start worker", func(t *testing.T) {
+		_, worker, _ := initQueue()
+		err := worker.start()
+		assert.Nil(t, err, "Worker should start successfully")
+	})
+
+	t.Run("Adding job to queue", func(t *testing.T) {
+		queue, _, internalQueue := initQueue()
+
+		// Test adding a job
+		job, ok := queue.Add("42")
+		assert.True(t, ok, "Job should be added successfully")
+		assert.NotNil(t, job, "Job should not be nil")
+		assert.Equal(t, 1, queue.PendingCount(), "Queue should have one pending job")
+		assert.Equal(t, 1, internalQueue.Len(), "Internal Queue should have one item")
+	})
+
+	t.Run("Processing job", func(t *testing.T) {
+		queue, worker, internalQueue := initQueue()
+
+		err := worker.start()
+		assert.Nil(t, err, "Worker should start successfully")
+
+		job, _ := queue.Add("42")
+		job.Result()
+		assert.Equal(t, 0, queue.PendingCount(), "Queue should have no pending jobs")
+		assert.Equal(t, 0, internalQueue.Len(), "Internal Queue should be empty")
+	})
+
+	t.Run("Adding multiple jobs to queue", func(t *testing.T) {
+		jobs := []Item[string]{
+			{Value: "1", ID: "job1"},
+			{Value: "2", ID: "job2"},
+			{Value: "3", ID: "job3"},
+			{Value: "4", ID: "job4"},
+			{Value: "5", ID: "job5"},
+		}
+
+		queue, worker, internalQueue := initQueue()
+
+		groupJob := queue.AddAll(jobs)
+		assert.Equal(t, 5, queue.PendingCount(), "Queue should have five pending jobs")
+		assert.Equal(t, 5, internalQueue.Len(), "Internal Queue should have five items")
+		assert.Equal(t, 5, groupJob.Len(), "Internal Queue should have five items")
+
+		err := worker.start()
+		assert.Nil(t, err, "Worker should start successfully")
+
+		results, _ := groupJob.Results()
+		_, err = groupJob.Results()
+		assert.NotNil(t, err, "Results channel should not be accessible more than once")
+
+		count := 0
+		for range results {
+			count++
+		}
+
+		assert.Equal(t, 5, count, "Results should have five items")
path: queue_test.go
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Update assertion to use dynamic count**

Update the assertion to use the dynamic count of successfully enqueued jobs instead of hardcoding "5".


```diff
-	assert.Equal(t, 5, count, "Results should have five items")
+	assert.Equal(t, addedCount, len(got), "Results count should match successfully enqueued jobs")
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
-       assert.Equal(t, 5, count, "Results should have five items")
+       assert.Equal(t, addedCount, len(got), "Results count should match successfully enqueued jobs")
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In queue_test.go at line 92, the assertion currently checks for a hardcoded
value of 5 for the count of results. Modify the assertion to use the dynamic
count variable that tracks the number of successfully enqueued jobs instead of
the fixed number 5, ensuring the test adapts to varying input sizes.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 33633b1
created_at: 2025-05-19T12:40:06Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-19T10:03:43Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: rate limited by coderabbit.ai -->

> [!WARNING]
> ## Rate limit exceeded
> 
> @fahimfaisaal has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait **7 minutes and 33 seconds** before requesting another review.
> 
> <details>
> <summary>⌛ How to resolve this issue?</summary>
> 
> After the wait time has elapsed, a review can be triggered using the `@coderabbitai review` command as a PR comment. Alternatively, push new commits to this PR.
> 
> We recommend that you space out your commits to avoid hitting the rate limit.
> 
> </details>
> 
> 
> <details>
> <summary>🚦 How do rate limits work?</summary>
> 
> CodeRabbit enforces hourly rate limits for each developer per organization.
> 
> Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.
> 
> Please see our [FAQ](https://docs.coderabbit.ai/faq) for further information.
> 
> </details>
> 
> <details>
> <summary>📥 Commits</summary>
> 
> Reviewing files that changed from the base of the PR and between ef5fd1a26f2144518c6c2079c3b14b48f5cef6ea and 33633b149670a83c17264441a9f16a6cb66addb8.
> 
> </details>
> 
> <details>
> <summary>📒 Files selected for processing (1)</summary>
> 
> * `queue_test.go` (1 hunks)
> 
> </details>

<!-- end of auto-generated comment: rate limited by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

The changes introduce explicit success/failure signaling for job addition methods across queue and priority queue implementations, updating their signatures to return a boolean alongside the job. Batch addition logic now checks enqueue results per job, skipping failed enqueues. Related types and interfaces are updated accordingly, and internal buffer size parameters are standardized to use `int` instead of `uint32`. Additional atomic job count tracking is added to group jobs. Benchmark tests are updated to verify enqueue success before accessing results. New tests for queue resizing are added. A panic check in worker start was removed.

## Changes

| File(s)                                             | Change Summary |
|----------------------------------------------------|----------------|
| `queue.go`, `priority.go`, `persistent.go`, `persistent_priority.go` | Updated `Add` and `AddAll` methods to return a boolean indicating enqueue success; batch addition methods now explicitly handle enqueue failures per job; signatures updated to use `Item` instead of `PQItem`; buffer sizes now use `int`. Added `Priority` field to `Item`. |
| `group_job.go`                                     | Added atomic job count tracking to `groupJob` via a new `groupLen` type; constructor and methods updated to use `int` for buffer size; new `Len()` method added; job count incremented/decremented atomically alongside wait group. |
| `result.go`                                        | Added method `Len() int` to `EnqueuedGroupJob` interface. |
| `result_channel.go`                                | Changed buffer size parameter type from `uint32` to `int` in `newResultChannel`. |
| `bench_test.go`                                    | Updated benchmarks to check enqueue success before calling `Result()`; adjusted types to use `Item` instead of `PQItem`; batch tests now check for errors from `Results()`. |
| `group_job_test.go`                                | Updated buffer size variables from `uint32` to `int` in tests; removed unnecessary type casts. |
| `internal/collections/queue_test.go`               | Added new subtests for explicit testing of the internal `resize` method under various conditions, verifying capacity, size, front index, and element order. |
| `queue_test.go`                                    | Added new unit tests for queue and worker operations including job addition, batch addition, processing, and result retrieval. |
| `worker.go`                                        | Removed panic check in `worker.start()` that enforced queue to be null queue at start. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Client
    participant Queue
    participant InternalQueue

    Client->>Queue: Add(data, configs...)
    Queue->>InternalQueue: Enqueue(job)
    alt Enqueue succeeds
        InternalQueue-->>Queue: true
        Queue-->>Client: (job, true)
    else Enqueue fails
        InternalQueue-->>Queue: false
        Queue-->>Client: (nil, false)
    end
```

```mermaid
sequenceDiagram
    participant Client
    participant Queue
    participant InternalQueue

    Client->>Queue: AddAll(items)
    loop for each item
        Queue->>InternalQueue: Enqueue(job)
        alt Enqueue succeeds
            InternalQueue-->>Queue: true
            Queue-->>GroupJob: add job
        else Enqueue fails
            InternalQueue-->>Queue: false
            Queue-->>GroupJob: skip job
        end
    end
    Queue-->>Client: EnqueuedGroupJob with successful jobs only
```

## Poem

> In the warren where queues align,  
> Now jobs report if things go fine.  
> With `Add` and `AddAll`, truth is told—  
> Success or failure, clear and bold.  
> Count your jobs, don’t guess or stall,  
> Rabbits code with care for all!  
> 🐇✨

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=goptics/varmq&utm_content=17):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: codecov[bot]
created_at: 2025-05-19T10:04:29Z
body: ## [Codecov](https://app.codecov.io/gh/goptics/varmq/pull/17?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goptics) Report
Attention: Patch coverage is `31.25000%` with `44 lines` in your changes missing coverage. Please review.
| [Files with missing lines](https://app.codecov.io/gh/goptics/varmq/pull/17?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goptics) | Patch % | Lines |
|---|---|---|
| [persistent.go](https://app.codecov.io/gh/goptics/varmq/pull/17?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goptics#diff-cGVyc2lzdGVudC5nbw==) | 0.00% | [13 Missing :warning: ](https://app.codecov.io/gh/goptics/varmq/pull/17?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goptics) |
| [persistent\_priority.go](https://app.codecov.io/gh/goptics/varmq/pull/17?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goptics#diff-cGVyc2lzdGVudF9wcmlvcml0eS5nbw==) | 0.00% | [13 Missing :warning: ](https://app.codecov.io/gh/goptics/varmq/pull/17?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goptics) |
| [priority.go](https://app.codecov.io/gh/goptics/varmq/pull/17?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goptics#diff-cHJpb3JpdHkuZ28=) | 0.00% | [11 Missing :warning: ](https://app.codecov.io/gh/goptics/varmq/pull/17?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goptics) |
| [queue.go](https://app.codecov.io/gh/goptics/varmq/pull/17?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goptics#diff-cXVldWUuZ28=) | 30.00% | [5 Missing and 2 partials :warning: ](https://app.codecov.io/gh/goptics/varmq/pull/17?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goptics) |

:loudspeaker: Thoughts on this report? [Let us know!](https://github.com/codecov/feedback/issues/255)
author_association: NONE