mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 15:43:49 +01:00
Add tests for: - Discord handlers (getPlayerSlice, getCharacterList) - House handlers (boxToBytes, HouseData, Title structs) - Mail struct tests - Mercenary handlers (Partner, HunterNavi structs) - Shop/Gacha handlers (writeShopItems, ShopItem, Gacha structs) - Additional handler coverage for guild, tower, and simple handlers - Stage handler tests for binary operations and enumeration - Channel server tests for BroadcastMHF and session management
748 lines
16 KiB
Go
748 lines
16 KiB
Go
package channelserver
|
|
|
|
import (
|
|
"net"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"erupe-ce/config"
|
|
"erupe-ce/network/mhfpacket"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func TestNewServer(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
DB: nil,
|
|
DiscordBot: nil,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
Name: "TestServer",
|
|
Enable: true,
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
|
|
if s == nil {
|
|
t.Fatal("NewServer returned nil")
|
|
}
|
|
|
|
// Check ID assignment
|
|
if s.ID != 1 {
|
|
t.Errorf("Server ID = %d, want 1", s.ID)
|
|
}
|
|
|
|
// Check name assignment
|
|
if s.name != "TestServer" {
|
|
t.Errorf("Server name = %s, want TestServer", s.name)
|
|
}
|
|
|
|
// Check channels are created
|
|
if s.acceptConns == nil {
|
|
t.Error("acceptConns channel is nil")
|
|
}
|
|
if s.deleteConns == nil {
|
|
t.Error("deleteConns channel is nil")
|
|
}
|
|
|
|
// Check maps are initialized
|
|
if s.sessions == nil {
|
|
t.Error("sessions map is nil")
|
|
}
|
|
if s.stages == nil {
|
|
t.Error("stages map is nil")
|
|
}
|
|
if s.userBinaryParts == nil {
|
|
t.Error("userBinaryParts map is nil")
|
|
}
|
|
if s.semaphore == nil {
|
|
t.Error("semaphore map is nil")
|
|
}
|
|
|
|
// Check semaphore index starts at 7 (skips reserved IDs)
|
|
if s.semaphoreIndex != 7 {
|
|
t.Errorf("semaphoreIndex = %d, want 7", s.semaphoreIndex)
|
|
}
|
|
|
|
// Check Raviente is initialized
|
|
if s.raviente == nil {
|
|
t.Error("raviente is nil")
|
|
}
|
|
}
|
|
|
|
func TestNewServer_DefaultStages(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
|
|
// Check persistent stages are created
|
|
expectedStages := []string{
|
|
"sl1Ns200p0a0u0", // Mezeporta
|
|
"sl1Ns211p0a0u0", // Rasta bar
|
|
"sl1Ns260p0a0u0", // Pallone Caravan
|
|
"sl1Ns262p0a0u0", // Pallone Guest House 1st Floor
|
|
"sl1Ns263p0a0u0", // Pallone Guest House 2nd Floor
|
|
"sl2Ns379p0a0u0", // Diva fountain
|
|
"sl1Ns462p0a0u0", // MezFes
|
|
}
|
|
|
|
for _, stageID := range expectedStages {
|
|
if _, ok := s.stages[stageID]; !ok {
|
|
t.Errorf("Expected default stage %s not found", stageID)
|
|
}
|
|
}
|
|
|
|
if len(s.stages) != len(expectedStages) {
|
|
t.Errorf("Server has %d stages, expected %d", len(s.stages), len(expectedStages))
|
|
}
|
|
}
|
|
|
|
func TestNewRaviente(t *testing.T) {
|
|
r := NewRaviente()
|
|
|
|
if r == nil {
|
|
t.Fatal("NewRaviente returned nil")
|
|
}
|
|
|
|
// Check register initialization
|
|
if r.register == nil {
|
|
t.Fatal("Raviente register is nil")
|
|
}
|
|
if r.register.nextTime != 0 {
|
|
t.Errorf("nextTime = %d, want 0", r.register.nextTime)
|
|
}
|
|
if r.register.maxPlayers != 0 {
|
|
t.Errorf("maxPlayers = %d, want 0", r.register.maxPlayers)
|
|
}
|
|
if len(r.register.register) != 5 {
|
|
t.Errorf("register array length = %d, want 5", len(r.register.register))
|
|
}
|
|
|
|
// Check state initialization
|
|
if r.state == nil {
|
|
t.Fatal("Raviente state is nil")
|
|
}
|
|
if len(r.state.stateData) != 29 {
|
|
t.Errorf("stateData length = %d, want 29", len(r.state.stateData))
|
|
}
|
|
|
|
// Check support initialization
|
|
if r.support == nil {
|
|
t.Fatal("Raviente support is nil")
|
|
}
|
|
if len(r.support.supportData) != 25 {
|
|
t.Errorf("supportData length = %d, want 25", len(r.support.supportData))
|
|
}
|
|
}
|
|
|
|
func TestRavienteRegister_InitialValues(t *testing.T) {
|
|
r := NewRaviente()
|
|
|
|
// All register slots should be 0 initially
|
|
for i, v := range r.register.register {
|
|
if v != 0 {
|
|
t.Errorf("register[%d] = %d, want 0", i, v)
|
|
}
|
|
}
|
|
|
|
// All state data should be 0 initially
|
|
for i, v := range r.state.stateData {
|
|
if v != 0 {
|
|
t.Errorf("stateData[%d] = %d, want 0", i, v)
|
|
}
|
|
}
|
|
|
|
// All support data should be 0 initially
|
|
for i, v := range r.support.supportData {
|
|
if v != 0 {
|
|
t.Errorf("supportData[%d] = %d, want 0", i, v)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestServerMutex(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
|
|
// Test that mutex works and doesn't deadlock
|
|
s.Lock()
|
|
s.isShuttingDown = true
|
|
s.Unlock()
|
|
|
|
s.Lock()
|
|
if !s.isShuttingDown {
|
|
t.Error("isShuttingDown should be true")
|
|
}
|
|
s.Unlock()
|
|
}
|
|
|
|
func TestServerStagesLock(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
|
|
// Test RWMutex for stages
|
|
s.stagesLock.RLock()
|
|
count := len(s.stages)
|
|
s.stagesLock.RUnlock()
|
|
|
|
if count < 7 {
|
|
t.Errorf("Expected at least 7 default stages, got %d", count)
|
|
}
|
|
|
|
// Test write lock
|
|
s.stagesLock.Lock()
|
|
s.stages["test_stage"] = NewStage("test_stage")
|
|
s.stagesLock.Unlock()
|
|
|
|
s.stagesLock.RLock()
|
|
if _, ok := s.stages["test_stage"]; !ok {
|
|
t.Error("test_stage not found after adding")
|
|
}
|
|
s.stagesLock.RUnlock()
|
|
}
|
|
|
|
func TestServerConcurrentStageAccess(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
// Multiple concurrent readers
|
|
for i := 0; i < 10; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for j := 0; j < 100; j++ {
|
|
s.stagesLock.RLock()
|
|
_ = len(s.stages)
|
|
s.stagesLock.RUnlock()
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Concurrent writer
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for j := 0; j < 50; j++ {
|
|
s.stagesLock.Lock()
|
|
stageID := "concurrent_test_" + string(rune('A'+j%26))
|
|
s.stages[stageID] = NewStage(stageID)
|
|
s.stagesLock.Unlock()
|
|
}
|
|
}()
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestNextSemaphoreID(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
|
|
// Initial index should be 7
|
|
if s.semaphoreIndex != 7 {
|
|
t.Errorf("Initial semaphoreIndex = %d, want 7", s.semaphoreIndex)
|
|
}
|
|
|
|
// Get next IDs
|
|
id1 := s.NextSemaphoreID()
|
|
id2 := s.NextSemaphoreID()
|
|
id3 := s.NextSemaphoreID()
|
|
|
|
// IDs should be unique and incrementing
|
|
if id1 == id2 || id2 == id3 || id1 == id3 {
|
|
t.Errorf("Semaphore IDs should be unique: %d, %d, %d", id1, id2, id3)
|
|
}
|
|
|
|
if id2 <= id1 || id3 <= id2 {
|
|
t.Errorf("Semaphore IDs should be incrementing: %d, %d, %d", id1, id2, id3)
|
|
}
|
|
}
|
|
|
|
func TestNextSemaphoreID_SkipsExisting(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
|
|
// Pre-populate some semaphores
|
|
s.semaphore["test1"] = &Semaphore{id: 8}
|
|
s.semaphore["test2"] = &Semaphore{id: 9}
|
|
|
|
id := s.NextSemaphoreID()
|
|
|
|
// Should skip 8 and 9 since they exist
|
|
if id == 8 || id == 9 {
|
|
t.Errorf("NextSemaphoreID should skip existing IDs, got %d", id)
|
|
}
|
|
}
|
|
|
|
func TestUserBinaryPartID(t *testing.T) {
|
|
id1 := userBinaryPartID{charID: 100, index: 1}
|
|
id2 := userBinaryPartID{charID: 100, index: 2}
|
|
id3 := userBinaryPartID{charID: 200, index: 1}
|
|
|
|
// Same char, different index should be different keys
|
|
if id1 == id2 {
|
|
t.Error("Different indices should produce different keys")
|
|
}
|
|
|
|
// Different char, same index should be different keys
|
|
if id1 == id3 {
|
|
t.Error("Different charIDs should produce different keys")
|
|
}
|
|
|
|
// Same values should be equal
|
|
id1copy := userBinaryPartID{charID: 100, index: 1}
|
|
if id1 != id1copy {
|
|
t.Error("Same values should be equal")
|
|
}
|
|
}
|
|
|
|
func TestServerUserBinaryParts(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
|
|
testData := []byte{0x01, 0x02, 0x03}
|
|
partID := userBinaryPartID{charID: 12345, index: 1}
|
|
|
|
// Store data
|
|
s.userBinaryPartsLock.Lock()
|
|
s.userBinaryParts[partID] = testData
|
|
s.userBinaryPartsLock.Unlock()
|
|
|
|
// Retrieve data
|
|
s.userBinaryPartsLock.RLock()
|
|
data, ok := s.userBinaryParts[partID]
|
|
s.userBinaryPartsLock.RUnlock()
|
|
|
|
if !ok {
|
|
t.Error("Failed to retrieve stored binary part")
|
|
}
|
|
if len(data) != 3 || data[0] != 0x01 {
|
|
t.Errorf("Retrieved data doesn't match: %v", data)
|
|
}
|
|
}
|
|
|
|
func TestServerShutdown(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
|
|
// Create a test listener
|
|
listener, err := net.Listen("tcp", ":0")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test listener: %v", err)
|
|
}
|
|
s.listener = listener
|
|
|
|
// Shutdown should not panic
|
|
s.Shutdown()
|
|
|
|
// Check shutdown flag is set
|
|
s.Lock()
|
|
if !s.isShuttingDown {
|
|
t.Error("isShuttingDown should be true after Shutdown()")
|
|
}
|
|
s.Unlock()
|
|
}
|
|
|
|
func TestServerFindSessionByCharID_NotFound(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
s.Channels = []*Server{s}
|
|
|
|
// Search for non-existent character
|
|
session := s.FindSessionByCharID(99999)
|
|
if session != nil {
|
|
t.Error("Expected nil for non-existent character")
|
|
}
|
|
}
|
|
|
|
func TestServerFindObjectByChar_NotFound(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
|
|
// Search for non-existent object
|
|
obj := s.FindObjectByChar(99999)
|
|
if obj != nil {
|
|
t.Error("Expected nil for non-existent object owner")
|
|
}
|
|
}
|
|
|
|
func TestServerStartAndShutdown(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
s.Port = 0 // Use any available port
|
|
|
|
err := s.Start()
|
|
if err != nil {
|
|
t.Fatalf("Server.Start() failed: %v", err)
|
|
}
|
|
|
|
// Give goroutines time to start
|
|
time.Sleep(10 * time.Millisecond)
|
|
|
|
// Verify listener is created
|
|
if s.listener == nil {
|
|
t.Error("Listener should be created after Start()")
|
|
}
|
|
|
|
// Shutdown
|
|
s.Shutdown()
|
|
|
|
// Give time for cleanup
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
|
|
func TestConfigStruct(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
|
|
cfg := &Config{
|
|
ID: 42,
|
|
Logger: logger,
|
|
DB: nil,
|
|
DiscordBot: nil,
|
|
ErupeConfig: &config.Config{},
|
|
Name: "Test Channel",
|
|
Enable: true,
|
|
}
|
|
|
|
if cfg.ID != 42 {
|
|
t.Errorf("Config ID = %d, want 42", cfg.ID)
|
|
}
|
|
if cfg.Name != "Test Channel" {
|
|
t.Errorf("Config Name = %s, want 'Test Channel'", cfg.Name)
|
|
}
|
|
if !cfg.Enable {
|
|
t.Error("Config Enable should be true")
|
|
}
|
|
}
|
|
|
|
func TestServerBroadcastMHF_NoSessions(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
|
|
// Should not panic with no sessions
|
|
pkt := &mhfpacket.MsgSysAck{
|
|
AckHandle: 1,
|
|
IsBufferResponse: false,
|
|
ErrorCode: 0,
|
|
AckData: []byte{0x00},
|
|
}
|
|
|
|
s.BroadcastMHF(pkt, nil)
|
|
}
|
|
|
|
func TestServerBroadcastMHF_WithSession(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
session := createMockSession(1, s)
|
|
s.sessions[nil] = session
|
|
|
|
pkt := &mhfpacket.MsgSysAck{
|
|
AckHandle: 1,
|
|
IsBufferResponse: false,
|
|
ErrorCode: 0,
|
|
AckData: []byte{0x00},
|
|
}
|
|
|
|
s.BroadcastMHF(pkt, nil)
|
|
|
|
// Check if packet was queued
|
|
select {
|
|
case p := <-session.sendPackets:
|
|
if p.data == nil {
|
|
t.Error("Packet data should not be nil")
|
|
}
|
|
default:
|
|
t.Error("No packet queued to session")
|
|
}
|
|
}
|
|
|
|
func TestServerBroadcastMHF_SkipsOrigin(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
session1 := createMockSession(1, s)
|
|
session2 := createMockSession(2, s)
|
|
s.sessions[nil] = session1
|
|
s.sessions[session2.rawConn] = session2
|
|
|
|
pkt := &mhfpacket.MsgSysAck{
|
|
AckHandle: 1,
|
|
IsBufferResponse: false,
|
|
ErrorCode: 0,
|
|
AckData: []byte{0x00},
|
|
}
|
|
|
|
// Broadcast from session1 - should skip session1
|
|
s.BroadcastMHF(pkt, session1)
|
|
|
|
// session1 should not receive the packet
|
|
select {
|
|
case <-session1.sendPackets:
|
|
t.Error("Origin session should not receive broadcast")
|
|
default:
|
|
// Good - no packet for origin
|
|
}
|
|
}
|
|
|
|
func TestServerFindSessionByCharID_Found(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
s.Channels = []*Server{s}
|
|
|
|
session := createMockSession(12345, s)
|
|
s.sessions[nil] = session
|
|
|
|
// Search for existing character
|
|
found := s.FindSessionByCharID(12345)
|
|
if found == nil {
|
|
t.Error("Expected to find session")
|
|
}
|
|
if found != session {
|
|
t.Error("Found wrong session")
|
|
}
|
|
}
|
|
|
|
func TestServerFindObjectByChar_Found(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
|
|
// Add a stage with an object
|
|
stage := NewStage("test_obj_stage")
|
|
obj := &Object{
|
|
id: 1,
|
|
ownerCharID: 12345,
|
|
x: 100.0,
|
|
y: 200.0,
|
|
z: 300.0,
|
|
}
|
|
stage.objects[obj.ownerCharID] = obj
|
|
s.stages["test_obj_stage"] = stage
|
|
|
|
// Search for existing object
|
|
found := s.FindObjectByChar(12345)
|
|
if found == nil {
|
|
t.Error("Expected to find object")
|
|
}
|
|
if found != obj {
|
|
t.Error("Found wrong object")
|
|
}
|
|
}
|
|
|
|
func TestServerConcurrentBinaryPartsAccess(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
// Concurrent writers
|
|
for i := 0; i < 10; i++ {
|
|
wg.Add(1)
|
|
go func(id int) {
|
|
defer wg.Done()
|
|
for j := 0; j < 10; j++ {
|
|
partID := userBinaryPartID{charID: uint32(id), index: uint8(j % 4)}
|
|
s.userBinaryPartsLock.Lock()
|
|
s.userBinaryParts[partID] = []byte{byte(id), byte(j)}
|
|
s.userBinaryPartsLock.Unlock()
|
|
}
|
|
}(i)
|
|
}
|
|
|
|
// Concurrent readers
|
|
for i := 0; i < 5; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for j := 0; j < 20; j++ {
|
|
s.userBinaryPartsLock.RLock()
|
|
_ = len(s.userBinaryParts)
|
|
s.userBinaryPartsLock.RUnlock()
|
|
}
|
|
}()
|
|
}
|
|
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestServerNextSemaphoreID(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
|
|
// Get multiple IDs and verify they're unique
|
|
ids := make(map[uint32]bool)
|
|
for i := 0; i < 10; i++ {
|
|
id := s.NextSemaphoreID()
|
|
if ids[id] {
|
|
t.Errorf("Duplicate semaphore ID: %d", id)
|
|
}
|
|
ids[id] = true
|
|
|
|
// IDs should be >= 7 (reserved indexes skipped)
|
|
if id < 7 {
|
|
t.Errorf("Semaphore ID %d should be >= 7", id)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestServerNextSemaphoreID_WrapAround(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
|
|
// Set semaphoreIndex to near max uint32 to test wrap-around
|
|
s.semaphoreIndex = ^uint32(0) - 1 // Max uint32 - 1
|
|
|
|
id1 := s.NextSemaphoreID()
|
|
id2 := s.NextSemaphoreID()
|
|
|
|
// After wrap-around, should skip to 7
|
|
if id1 < 7 && id1 != 0 {
|
|
t.Errorf("After wrap-around, first ID should be >= 7, got %d", id1)
|
|
}
|
|
|
|
if id1 == id2 {
|
|
t.Error("IDs should be different")
|
|
}
|
|
}
|
|
|
|
func TestServerNextSemaphoreID_SkipsExisting(t *testing.T) {
|
|
logger, _ := zap.NewDevelopment()
|
|
cfg := &Config{
|
|
ID: 1,
|
|
Logger: logger,
|
|
ErupeConfig: &config.Config{DevMode: true},
|
|
}
|
|
|
|
s := NewServer(cfg)
|
|
|
|
// Pre-populate with some semaphores
|
|
for i := uint32(7); i <= 15; i++ {
|
|
s.semaphore["test_"+string(rune('a'+i))] = &Semaphore{id: i}
|
|
}
|
|
|
|
// Get a new ID - should skip the existing ones
|
|
id := s.NextSemaphoreID()
|
|
|
|
// Verify ID is not one of the existing ones
|
|
for _, sema := range s.semaphore {
|
|
if sema.id == id {
|
|
t.Errorf("NextSemaphoreID returned existing ID: %d", id)
|
|
}
|
|
}
|
|
}
|