myxce/src/frontend/apiserver/controllers/queue_control_test.go
Rongsong Shen 8124cda83e feat: P0-2b implement ControlHost via xcecli_hostctrl + fix allFlag race
- backend/clib.go: Replace ControlHost stub with real implementation
  using xcecli_hostctrl, following the same pattern as ControlQueue
- controllers/hosts.go: Remove package-level allFlag variable;
  preCheckHostControlRequest now returns (bool, error) so the allFlag
  is a local value in the caller, eliminating the data race
- controllers/queues.go: Same fix for preCheckQueueControlRequest
- Add unit tests for both preCheck functions verifying return values,
  error cases, and concurrent safety
- Add compile-time verification test for ControlHost stub signature
2026-04-17 08:17:56 +00:00

139 lines
3.4 KiB
Go

package controllers
import (
"apiserver/models"
"apiserver/store"
"apiserver/utils"
"sync"
"testing"
"go.uber.org/zap"
)
func resetQueueControlState() {
store.InitStores()
utils.Log = zap.NewNop()
}
// TestPreCheckQueueControlRequest_AllFlag verifies that when queue_name is
// ["all"], the returned bool is true.
func TestPreCheckQueueControlRequest_AllFlag(t *testing.T) {
resetQueueControlState()
req := &models.QueueControlRequest{
ControlAction: "close",
QueueName: []string{"all"},
}
allFlag, err := preCheckQueueControlRequest(req)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !allFlag {
t.Error("expected allFlag=true when queue_name is [\"all\"]")
}
}
// TestPreCheckQueueControlRequest_NotAll verifies that when queue_name is a
// specific queue, the returned bool is false.
func TestPreCheckQueueControlRequest_NotAll(t *testing.T) {
resetQueueControlState()
req := &models.QueueControlRequest{
ControlAction: "open",
QueueName: []string{"normal"},
}
allFlag, err := preCheckQueueControlRequest(req)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if allFlag {
t.Error("expected allFlag=false when queue_name is [\"normal\"]")
}
}
// TestPreCheckQueueControlRequest_NoRace verifies that concurrent calls to
// preCheckQueueControlRequest do not share state via a package-level variable.
func TestPreCheckQueueControlRequest_NoRace(t *testing.T) {
t.Parallel()
resetQueueControlState()
var wg sync.WaitGroup
const goroutines = 50
results := make([]bool, goroutines)
errors := make([]error, goroutines)
for i := 0; i < goroutines; i++ {
wg.Add(1)
go func(idx int) {
defer wg.Done()
var req models.QueueControlRequest
if idx%2 == 0 {
req = models.QueueControlRequest{
ControlAction: "close",
QueueName: []string{"all"},
}
} else {
req = models.QueueControlRequest{
ControlAction: "open",
QueueName: []string{"normal"},
}
}
results[idx], errors[idx] = preCheckQueueControlRequest(&req)
}(i)
}
wg.Wait()
for i := 0; i < goroutines; i++ {
if errors[i] != nil {
t.Errorf("goroutine %d: unexpected error: %v", i, errors[i])
continue
}
if i%2 == 0 && !results[i] {
t.Errorf("goroutine %d: expected allFlag=true for [\"all\"]", i)
}
if i%2 != 0 && results[i] {
t.Errorf("goroutine %d: expected allFlag=false for [\"normal\"]", i)
}
}
}
// TestPreCheckQueueControlRequest_InvalidAction verifies that an unsupported
// action returns an error.
func TestPreCheckQueueControlRequest_InvalidAction(t *testing.T) {
resetQueueControlState()
req := &models.QueueControlRequest{
ControlAction: "invalid_action",
QueueName: []string{"normal"},
}
_, err := preCheckQueueControlRequest(req)
if err == nil {
t.Error("expected error for invalid action, got nil")
}
}
// TestPreCheckQueueControlRequest_CommentTooLong verifies that a comment
// exceeding 512 characters returns an error.
func TestPreCheckQueueControlRequest_CommentTooLong(t *testing.T) {
resetQueueControlState()
longComment := make([]byte, 513)
for i := range longComment {
longComment[i] = 'a'
}
req := &models.QueueControlRequest{
ControlAction: "close",
QueueName: []string{"normal"},
Comment: string(longComment),
}
_, err := preCheckQueueControlRequest(req)
if err == nil {
t.Error("expected error for comment exceeding 512 chars, got nil")
}
}