503 lines
12 KiB
Go
503 lines
12 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestNew(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
config *Config
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "New with nil config uses default",
|
|
config: nil,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "New with custom config",
|
|
config: createTestConfig(),
|
|
expectError: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
server, err := New(tt.config)
|
|
if (err != nil) != tt.expectError {
|
|
t.Errorf("New() error = %v, expectError %v", err, tt.expectError)
|
|
|
|
return
|
|
}
|
|
if server == nil && !tt.expectError {
|
|
t.Error("New() returned nil server")
|
|
|
|
return
|
|
}
|
|
if server != nil && server.config == nil {
|
|
t.Error("New() server.config is nil")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewInitializesStreamsAndState(t *testing.T) {
|
|
config := createTestConfig()
|
|
server, err := New(config)
|
|
if err != nil {
|
|
t.Fatalf("New() failed: %v", err)
|
|
}
|
|
|
|
// Verify streams are initialized
|
|
if len(server.streams) != len(config.Profiles) {
|
|
t.Errorf("Expected %d streams, got %d", len(config.Profiles), len(server.streams))
|
|
}
|
|
|
|
// Verify each stream has correct configuration
|
|
for _, profile := range config.Profiles {
|
|
stream, ok := server.streams[profile.Token]
|
|
if !ok {
|
|
t.Errorf("Stream not found for profile %s", profile.Token)
|
|
|
|
continue
|
|
}
|
|
if stream.ProfileToken != profile.Token {
|
|
t.Errorf("Stream profile token mismatch: %s != %s", stream.ProfileToken, profile.Token)
|
|
}
|
|
}
|
|
|
|
// Verify PTZ state is initialized for profiles with PTZ
|
|
for _, profile := range config.Profiles {
|
|
if profile.PTZ != nil {
|
|
_, ok := server.ptzState[profile.Token]
|
|
if !ok {
|
|
t.Errorf("PTZ state not found for profile %s", profile.Token)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify imaging state is initialized
|
|
if len(server.imagingState) != len(config.Profiles) {
|
|
t.Errorf("Expected %d imaging states, got %d", len(config.Profiles), len(server.imagingState))
|
|
}
|
|
}
|
|
|
|
func TestGetConfig(t *testing.T) {
|
|
config := createTestConfig()
|
|
server, _ := New(config)
|
|
|
|
got := server.GetConfig()
|
|
if got != config {
|
|
t.Error("GetConfig() returned different config")
|
|
}
|
|
if got.Profiles[0].Name != config.Profiles[0].Name {
|
|
t.Errorf("GetConfig() profile name mismatch: %s != %s", got.Profiles[0].Name, config.Profiles[0].Name)
|
|
}
|
|
}
|
|
|
|
func TestGetStreamConfig(t *testing.T) {
|
|
config := createTestConfig()
|
|
server, _ := New(config)
|
|
|
|
profileToken := config.Profiles[0].Token
|
|
|
|
tests := []struct {
|
|
name string
|
|
token string
|
|
expectOk bool
|
|
checkFunc func(*StreamConfig) error
|
|
}{
|
|
{
|
|
name: "Get existing stream",
|
|
token: profileToken,
|
|
expectOk: true,
|
|
checkFunc: func(sc *StreamConfig) error {
|
|
if sc.ProfileToken != profileToken {
|
|
return errorf("profile token mismatch: %s != %s", sc.ProfileToken, profileToken)
|
|
}
|
|
if sc.StreamURI == "" {
|
|
return errorf("StreamURI is empty")
|
|
}
|
|
|
|
return nil
|
|
},
|
|
},
|
|
{
|
|
name: "Get non-existent stream",
|
|
token: "invalid-token",
|
|
expectOk: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
stream, ok := server.GetStreamConfig(tt.token)
|
|
if ok != tt.expectOk {
|
|
t.Errorf("GetStreamConfig() ok = %v, expectOk %v", ok, tt.expectOk)
|
|
|
|
return
|
|
}
|
|
if ok && tt.checkFunc != nil {
|
|
if err := tt.checkFunc(stream); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUpdateStreamURI(t *testing.T) {
|
|
config := createTestConfig()
|
|
server, _ := New(config)
|
|
profileToken := config.Profiles[0].Token
|
|
|
|
tests := []struct {
|
|
name string
|
|
token string
|
|
newURI string
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "Update existing stream URI",
|
|
token: profileToken,
|
|
newURI: "rtsp://localhost:8554/newstream",
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Update non-existent stream",
|
|
token: "invalid-token",
|
|
newURI: "rtsp://localhost:8554/stream",
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := server.UpdateStreamURI(tt.token, tt.newURI)
|
|
if (err != nil) != tt.expectError {
|
|
t.Errorf("UpdateStreamURI() error = %v, expectError %v", err, tt.expectError)
|
|
|
|
return
|
|
}
|
|
if !tt.expectError {
|
|
stream, _ := server.GetStreamConfig(tt.token)
|
|
if stream.StreamURI != tt.newURI {
|
|
t.Errorf("UpdateStreamURI() failed: %s != %s", stream.StreamURI, tt.newURI)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListProfiles(t *testing.T) {
|
|
config := createTestConfig()
|
|
server, _ := New(config)
|
|
|
|
profiles := server.ListProfiles()
|
|
|
|
if len(profiles) != len(config.Profiles) {
|
|
t.Errorf("ListProfiles() length = %d, want %d", len(profiles), len(config.Profiles))
|
|
}
|
|
|
|
for i, profile := range profiles {
|
|
if profile.Token != config.Profiles[i].Token {
|
|
t.Errorf("ListProfiles()[%d] token mismatch: %s != %s", i, profile.Token, config.Profiles[i].Token)
|
|
}
|
|
if profile.Name != config.Profiles[i].Name {
|
|
t.Errorf("ListProfiles()[%d] name mismatch: %s != %s", i, profile.Name, config.Profiles[i].Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGetPTZState(t *testing.T) {
|
|
config := createTestConfig()
|
|
server, _ := New(config)
|
|
|
|
// Find a profile with PTZ
|
|
var profileWithPTZ string
|
|
for _, profile := range config.Profiles {
|
|
if profile.PTZ != nil {
|
|
profileWithPTZ = profile.Token
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
if profileWithPTZ == "" {
|
|
// Create config with PTZ
|
|
config.Profiles[0].PTZ = &PTZConfig{
|
|
NodeToken: "ptz_node",
|
|
PanRange: Range{Min: -360, Max: 360},
|
|
TiltRange: Range{Min: -90, Max: 90},
|
|
ZoomRange: Range{Min: 0, Max: 10},
|
|
}
|
|
server, _ = New(config)
|
|
profileWithPTZ = config.Profiles[0].Token
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
token string
|
|
expectOk bool
|
|
}{
|
|
{
|
|
name: "Get PTZ state for profile with PTZ",
|
|
token: profileWithPTZ,
|
|
expectOk: true,
|
|
},
|
|
{
|
|
name: "Get PTZ state for non-existent profile",
|
|
token: "invalid-token",
|
|
expectOk: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
state, ok := server.GetPTZState(tt.token)
|
|
if ok != tt.expectOk {
|
|
t.Errorf("GetPTZState() ok = %v, expectOk %v", ok, tt.expectOk)
|
|
|
|
return
|
|
}
|
|
if ok && state == nil {
|
|
t.Error("GetPTZState() returned nil state")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetImagingState(t *testing.T) {
|
|
config := createTestConfig()
|
|
server, _ := New(config)
|
|
|
|
videoSourceToken := config.Profiles[0].VideoSource.Token
|
|
|
|
tests := []struct {
|
|
name string
|
|
token string
|
|
expectOk bool
|
|
checkFunc func(*ImagingState) error
|
|
}{
|
|
{
|
|
name: "Get imaging state for existing source",
|
|
token: videoSourceToken,
|
|
expectOk: true,
|
|
checkFunc: func(state *ImagingState) error {
|
|
if state.Brightness < 0 || state.Brightness > 100 {
|
|
return errorf("brightness out of range: %f", state.Brightness)
|
|
}
|
|
if state.Contrast < 0 || state.Contrast > 100 {
|
|
return errorf("contrast out of range: %f", state.Contrast)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
},
|
|
{
|
|
name: "Get imaging state for non-existent source",
|
|
token: "invalid-token",
|
|
expectOk: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
state, ok := server.GetImagingState(tt.token)
|
|
if ok != tt.expectOk {
|
|
t.Errorf("GetImagingState() ok = %v, expectOk %v", ok, tt.expectOk)
|
|
|
|
return
|
|
}
|
|
if ok && tt.checkFunc != nil {
|
|
if err := tt.checkFunc(state); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestServerInfo(t *testing.T) {
|
|
config := createTestConfig()
|
|
server, _ := New(config)
|
|
|
|
info := server.ServerInfo()
|
|
|
|
if info == "" {
|
|
t.Error("ServerInfo() returned empty string")
|
|
}
|
|
|
|
// Check that key information is present
|
|
if !contains(info, config.DeviceInfo.Manufacturer) {
|
|
t.Errorf("ServerInfo() missing manufacturer: %s", config.DeviceInfo.Manufacturer)
|
|
}
|
|
if !contains(info, config.DeviceInfo.Model) {
|
|
t.Errorf("ServerInfo() missing model: %s", config.DeviceInfo.Model)
|
|
}
|
|
if !contains(info, config.Profiles[0].Name) {
|
|
t.Errorf("ServerInfo() missing profile name: %s", config.Profiles[0].Name)
|
|
}
|
|
}
|
|
|
|
func TestStartContextTimeout(t *testing.T) {
|
|
config := createTestConfig()
|
|
config.Port = 0 // Use random port
|
|
server, _ := New(config)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
|
defer cancel()
|
|
|
|
// Start should return due to context timeout
|
|
err := server.Start(ctx)
|
|
if err != nil {
|
|
t.Logf("Start() error (expected): %v", err)
|
|
}
|
|
}
|
|
|
|
// Helper functions
|
|
|
|
func createTestConfig() *Config {
|
|
return &Config{
|
|
Host: "127.0.0.1",
|
|
Port: 8080,
|
|
BasePath: "/onvif",
|
|
Timeout: 30 * time.Second,
|
|
DeviceInfo: DeviceInfo{
|
|
Manufacturer: "Test",
|
|
Model: "TestCamera",
|
|
FirmwareVersion: "1.0.0",
|
|
SerialNumber: "12345",
|
|
HardwareID: "HW001",
|
|
},
|
|
Username: "admin",
|
|
Password: "password",
|
|
Profiles: []ProfileConfig{
|
|
{
|
|
Token: "profile_token_1",
|
|
Name: "Profile 1",
|
|
VideoSource: VideoSourceConfig{
|
|
Token: "video_source_1",
|
|
Name: "Video Source 1",
|
|
Resolution: Resolution{Width: 1920, Height: 1080},
|
|
Framerate: 30,
|
|
Bounds: Bounds{
|
|
X: 0,
|
|
Y: 0,
|
|
Width: 1920,
|
|
Height: 1080,
|
|
},
|
|
},
|
|
VideoEncoder: VideoEncoderConfig{
|
|
Encoding: "H264",
|
|
Resolution: Resolution{Width: 1920, Height: 1080},
|
|
Quality: 80,
|
|
Framerate: 30,
|
|
Bitrate: 2048,
|
|
GovLength: 30,
|
|
},
|
|
PTZ: &PTZConfig{
|
|
NodeToken: "ptz_node_1",
|
|
PanRange: Range{Min: -360, Max: 360},
|
|
TiltRange: Range{Min: -90, Max: 90},
|
|
ZoomRange: Range{Min: 0, Max: 10},
|
|
},
|
|
Snapshot: SnapshotConfig{
|
|
Enabled: true,
|
|
Resolution: Resolution{Width: 1920, Height: 1080},
|
|
Quality: 85.0,
|
|
},
|
|
},
|
|
},
|
|
SupportPTZ: true,
|
|
SupportImaging: true,
|
|
SupportEvents: false,
|
|
}
|
|
}
|
|
|
|
func contains(s, substr string) bool {
|
|
for i := 0; i < len(s)-len(substr)+1; i++ {
|
|
if s[i:i+len(substr)] == substr {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
type testError struct {
|
|
msg string
|
|
}
|
|
|
|
func (e *testError) Error() string {
|
|
return e.msg
|
|
}
|
|
|
|
func errorf(format string, args ...interface{}) error {
|
|
return &testError{msg: fmt.Sprintf(format, args...)}
|
|
}
|
|
|
|
func TestServerInfoMethod(t *testing.T) {
|
|
config := createTestConfig()
|
|
server, _ := New(config)
|
|
|
|
info := server.ServerInfo()
|
|
|
|
if info == "" {
|
|
t.Fatal("ServerInfo() returned empty string")
|
|
}
|
|
|
|
// ServerInfo returns a formatted string with server information
|
|
if !strings.Contains(info, "127.0.0.1") && !strings.Contains(info, "localhost") {
|
|
t.Logf("ServerInfo may not contain host: %s", info)
|
|
}
|
|
}
|
|
|
|
func TestGettersAndSetters(t *testing.T) {
|
|
config := createTestConfig()
|
|
server, _ := New(config)
|
|
|
|
// Test GetConfig
|
|
cfg := server.GetConfig()
|
|
if cfg == nil {
|
|
t.Error("GetConfig returned nil")
|
|
}
|
|
|
|
// Test GetStreamConfig
|
|
streamCfg, _ := server.GetStreamConfig(config.Profiles[0].Token)
|
|
if streamCfg == nil {
|
|
t.Error("GetStreamConfig returned nil")
|
|
}
|
|
|
|
// Test UpdateStreamURI
|
|
newURI := "rtsp://example.com/stream"
|
|
server.UpdateStreamURI(config.Profiles[0].Token, newURI)
|
|
updated, _ := server.GetStreamConfig(config.Profiles[0].Token)
|
|
if updated.StreamURI != newURI {
|
|
t.Errorf("UpdateStreamURI failed: got %s, want %s", updated.StreamURI, newURI)
|
|
}
|
|
|
|
// Test ListProfiles
|
|
profiles := server.ListProfiles()
|
|
if len(profiles) == 0 {
|
|
t.Error("ListProfiles returned empty list")
|
|
}
|
|
|
|
// Test GetPTZState
|
|
ptzState, _ := server.GetPTZState(config.Profiles[0].Token)
|
|
if ptzState == nil {
|
|
t.Error("GetPTZState returned nil")
|
|
}
|
|
|
|
// Test GetImagingState
|
|
imgState, _ := server.GetImagingState(config.Profiles[0].VideoSource.Token)
|
|
if imgState == nil {
|
|
t.Error("GetImagingState returned nil")
|
|
}
|
|
}
|