440 lines
10 KiB
Go
440 lines
10 KiB
Go
/*
|
|
* ============================================================================
|
|
* Projekt.....: rs2322tcp
|
|
* Datei.......: session_test.go
|
|
* Copyright (C) 2026 Dieter Lang
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*
|
|
* Beschreibung:
|
|
* Tests für den Lebenszyklus einer Client-Session, die Verwaltung
|
|
* der zugehörigen Ressourcen und der dynamischen Data-Listener.
|
|
* ============================================================================
|
|
*/
|
|
package server_test
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"testing"
|
|
|
|
"git.lang-dieter.de/rs2322tcp/internal/server"
|
|
)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Test resource
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
type testResource struct {
|
|
closed bool
|
|
err error
|
|
}
|
|
|
|
func (r *testResource) Close() error {
|
|
r.closed = true
|
|
return r.err
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Constructor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
func TestNewSession(t *testing.T) {
|
|
serverConn, clientConn := net.Pipe()
|
|
defer serverConn.Close()
|
|
defer clientConn.Close()
|
|
|
|
session, err := server.NewSession(1, serverConn)
|
|
if err != nil {
|
|
t.Fatalf("NewSession() failed: %v", err)
|
|
}
|
|
|
|
if session.ID() != 1 {
|
|
t.Errorf("ID() = %d, want 1", session.ID())
|
|
}
|
|
|
|
if session.Conn() == nil {
|
|
t.Fatal("Conn() returned nil")
|
|
}
|
|
|
|
if session.IsClosed() {
|
|
t.Fatal("new session is already closed")
|
|
}
|
|
|
|
if session.ResourceCount() != 0 {
|
|
t.Errorf("ResourceCount() = %d, want 0",
|
|
session.ResourceCount())
|
|
}
|
|
|
|
if session.DataListenerCount() != 0 {
|
|
t.Errorf("DataListenerCount() = %d, want 0",
|
|
session.DataListenerCount())
|
|
}
|
|
}
|
|
|
|
func TestNewSessionRejectsNilConnection(t *testing.T) {
|
|
session, err := server.NewSession(1, nil)
|
|
|
|
if err == nil {
|
|
t.Fatal("NewSession() succeeded, want error")
|
|
}
|
|
|
|
if session != nil {
|
|
t.Fatal("NewSession() returned a session despite error")
|
|
}
|
|
}
|
|
|
|
func TestNewSessionRejectsZeroID(t *testing.T) {
|
|
serverConn, clientConn := net.Pipe()
|
|
defer serverConn.Close()
|
|
defer clientConn.Close()
|
|
|
|
session, err := server.NewSession(0, serverConn)
|
|
|
|
if err == nil {
|
|
t.Fatal("NewSession() succeeded, want error")
|
|
}
|
|
|
|
if session != nil {
|
|
t.Fatal("NewSession() returned a session despite error")
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Resources
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
func TestSessionAddResource(t *testing.T) {
|
|
serverConn, clientConn := net.Pipe()
|
|
defer serverConn.Close()
|
|
defer clientConn.Close()
|
|
|
|
session, err := server.NewSession(1, serverConn)
|
|
if err != nil {
|
|
t.Fatalf("NewSession() failed: %v", err)
|
|
}
|
|
|
|
resource1 := &testResource{}
|
|
resource2 := &testResource{}
|
|
|
|
if err := session.AddResource(resource1); err != nil {
|
|
t.Fatalf("AddResource(resource1) failed: %v", err)
|
|
}
|
|
|
|
if err := session.AddResource(resource2); err != nil {
|
|
t.Fatalf("AddResource(resource2) failed: %v", err)
|
|
}
|
|
|
|
if session.ResourceCount() != 2 {
|
|
t.Errorf("ResourceCount() = %d, want 2",
|
|
session.ResourceCount())
|
|
}
|
|
|
|
if resource1.closed {
|
|
t.Fatal("resource1 was closed before session.Close()")
|
|
}
|
|
|
|
if resource2.closed {
|
|
t.Fatal("resource2 was closed before session.Close()")
|
|
}
|
|
}
|
|
|
|
func TestAddNilResource(t *testing.T) {
|
|
serverConn, clientConn := net.Pipe()
|
|
defer serverConn.Close()
|
|
defer clientConn.Close()
|
|
|
|
session, err := server.NewSession(1, serverConn)
|
|
if err != nil {
|
|
t.Fatalf("NewSession() failed: %v", err)
|
|
}
|
|
|
|
if err := session.AddResource(nil); err == nil {
|
|
t.Fatal("AddResource(nil) succeeded, want error")
|
|
}
|
|
}
|
|
|
|
func TestAddResourceAfterClose(t *testing.T) {
|
|
serverConn, clientConn := net.Pipe()
|
|
defer clientConn.Close()
|
|
|
|
session, err := server.NewSession(1, serverConn)
|
|
if err != nil {
|
|
t.Fatalf("NewSession() failed: %v", err)
|
|
}
|
|
|
|
if err := session.Close(); err != nil {
|
|
t.Fatalf("Close() failed: %v", err)
|
|
}
|
|
|
|
resource := &testResource{}
|
|
|
|
if err := session.AddResource(resource); err == nil {
|
|
t.Fatal("AddResource() succeeded, want error")
|
|
}
|
|
|
|
if !resource.closed {
|
|
t.Fatal("resource was not closed after rejected AddResource()")
|
|
}
|
|
|
|
if session.ResourceCount() != 0 {
|
|
t.Errorf("ResourceCount() = %d, want 0",
|
|
session.ResourceCount())
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Data listeners
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
func TestSessionAddDataListener(t *testing.T) {
|
|
serverConn, clientConn := net.Pipe()
|
|
defer clientConn.Close()
|
|
|
|
session, err := server.NewSession(1, serverConn)
|
|
if err != nil {
|
|
t.Fatalf("NewSession() failed: %v", err)
|
|
}
|
|
|
|
listener, err := server.NewDataListener()
|
|
if err != nil {
|
|
t.Fatalf("NewDataListener() failed: %v", err)
|
|
}
|
|
|
|
port := listener.Port()
|
|
|
|
if port == 0 {
|
|
t.Fatal("DataListener.Port() returned 0")
|
|
}
|
|
|
|
if err := session.AddDataListener("radio", listener); err != nil {
|
|
t.Fatalf("AddDataListener() failed: %v", err)
|
|
}
|
|
|
|
if session.DataListenerCount() != 1 {
|
|
t.Errorf("DataListenerCount() = %d, want 1",
|
|
session.DataListenerCount())
|
|
}
|
|
|
|
if session.ResourceCount() != 1 {
|
|
t.Errorf("ResourceCount() = %d, want 1",
|
|
session.ResourceCount())
|
|
}
|
|
|
|
storedListener, ok := session.DataListener("radio")
|
|
if !ok {
|
|
t.Fatal("DataListener(radio) not found")
|
|
}
|
|
|
|
if storedListener != listener {
|
|
t.Fatal("stored listener is not the same listener")
|
|
}
|
|
|
|
if session.DataPort("radio") != port {
|
|
t.Errorf("DataPort(radio) = %d, want %d",
|
|
session.DataPort("radio"), port)
|
|
}
|
|
|
|
if session.DataPort("unknown") != 0 {
|
|
t.Errorf("DataPort(unknown) = %d, want 0",
|
|
session.DataPort("unknown"))
|
|
}
|
|
}
|
|
|
|
func TestSessionRejectsDuplicateDataListener(t *testing.T) {
|
|
serverConn, clientConn := net.Pipe()
|
|
defer clientConn.Close()
|
|
|
|
session, err := server.NewSession(1, serverConn)
|
|
if err != nil {
|
|
t.Fatalf("NewSession() failed: %v", err)
|
|
}
|
|
|
|
listener1, err := server.NewDataListener()
|
|
if err != nil {
|
|
t.Fatalf("NewDataListener() #1 failed: %v", err)
|
|
}
|
|
|
|
listener2, err := server.NewDataListener()
|
|
if err != nil {
|
|
t.Fatalf("NewDataListener() #2 failed: %v", err)
|
|
}
|
|
|
|
if err := session.AddDataListener("radio", listener1); err != nil {
|
|
t.Fatalf("AddDataListener() #1 failed: %v", err)
|
|
}
|
|
|
|
if err := session.AddDataListener("radio", listener2); err == nil {
|
|
t.Fatal("AddDataListener() #2 succeeded, want error")
|
|
}
|
|
|
|
if listener2.Port() != 0 {
|
|
t.Errorf("duplicate listener port = %d, want 0",
|
|
listener2.Port())
|
|
}
|
|
|
|
if session.DataListenerCount() != 1 {
|
|
t.Errorf("DataListenerCount() = %d, want 1",
|
|
session.DataListenerCount())
|
|
}
|
|
}
|
|
|
|
func TestSessionRejectsEmptyDeviceID(t *testing.T) {
|
|
serverConn, clientConn := net.Pipe()
|
|
defer clientConn.Close()
|
|
|
|
session, err := server.NewSession(1, serverConn)
|
|
if err != nil {
|
|
t.Fatalf("NewSession() failed: %v", err)
|
|
}
|
|
|
|
listener, err := server.NewDataListener()
|
|
if err != nil {
|
|
t.Fatalf("NewDataListener() failed: %v", err)
|
|
}
|
|
|
|
if err := session.AddDataListener("", listener); err == nil {
|
|
t.Fatal("AddDataListener() succeeded, want error")
|
|
}
|
|
|
|
if listener.Port() != 0 {
|
|
t.Errorf("listener port = %d, want 0",
|
|
listener.Port())
|
|
}
|
|
}
|
|
|
|
func TestSessionClosesDataListener(t *testing.T) {
|
|
serverConn, clientConn := net.Pipe()
|
|
defer clientConn.Close()
|
|
|
|
session, err := server.NewSession(1, serverConn)
|
|
if err != nil {
|
|
t.Fatalf("NewSession() failed: %v", err)
|
|
}
|
|
|
|
dataListener, err := server.NewDataListener()
|
|
if err != nil {
|
|
t.Fatalf("NewDataListener() failed: %v", err)
|
|
}
|
|
|
|
if err := session.AddDataListener("radio", dataListener); err != nil {
|
|
t.Fatalf("AddDataListener() failed: %v", err)
|
|
}
|
|
|
|
if session.DataListenerCount() != 1 {
|
|
t.Fatalf("DataListenerCount() = %d, want 1",
|
|
session.DataListenerCount())
|
|
}
|
|
|
|
if err := session.Close(); err != nil {
|
|
t.Fatalf("Session.Close() failed: %v", err)
|
|
}
|
|
|
|
if dataListener.Port() != 0 {
|
|
t.Errorf("DataListener.Port() after Session.Close() = %d, want 0",
|
|
dataListener.Port())
|
|
}
|
|
|
|
if session.DataListenerCount() != 0 {
|
|
t.Errorf("DataListenerCount() after Session.Close() = %d, want 0",
|
|
session.DataListenerCount())
|
|
}
|
|
|
|
if session.DataPort("radio") != 0 {
|
|
t.Errorf("DataPort(radio) after Close() = %d, want 0",
|
|
session.DataPort("radio"))
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Close
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
func TestSessionClose(t *testing.T) {
|
|
serverConn, clientConn := net.Pipe()
|
|
defer clientConn.Close()
|
|
|
|
session, err := server.NewSession(1, serverConn)
|
|
if err != nil {
|
|
t.Fatalf("NewSession() failed: %v", err)
|
|
}
|
|
|
|
resource1 := &testResource{}
|
|
resource2 := &testResource{}
|
|
|
|
if err := session.AddResource(resource1); err != nil {
|
|
t.Fatalf("AddResource(resource1) failed: %v", err)
|
|
}
|
|
|
|
if err := session.AddResource(resource2); err != nil {
|
|
t.Fatalf("AddResource(resource2) failed: %v", err)
|
|
}
|
|
|
|
if err := session.Close(); err != nil {
|
|
t.Fatalf("Close() failed: %v", err)
|
|
}
|
|
|
|
if !session.IsClosed() {
|
|
t.Fatal("session is not closed")
|
|
}
|
|
|
|
if session.Conn() != nil {
|
|
t.Fatal("Conn() returned a connection after Close()")
|
|
}
|
|
|
|
if session.ResourceCount() != 0 {
|
|
t.Errorf("ResourceCount() after Close() = %d, want 0",
|
|
session.ResourceCount())
|
|
}
|
|
|
|
if !resource1.closed {
|
|
t.Fatal("resource1 was not closed")
|
|
}
|
|
|
|
if !resource2.closed {
|
|
t.Fatal("resource2 was not closed")
|
|
}
|
|
|
|
if err := session.Close(); err != nil {
|
|
t.Fatalf("second Close() failed: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSessionCloseResourceError(t *testing.T) {
|
|
serverConn, clientConn := net.Pipe()
|
|
defer clientConn.Close()
|
|
|
|
session, err := server.NewSession(1, serverConn)
|
|
if err != nil {
|
|
t.Fatalf("NewSession() failed: %v", err)
|
|
}
|
|
|
|
expectedErr := errors.New("test resource error")
|
|
|
|
resource := &testResource{
|
|
err: expectedErr,
|
|
}
|
|
|
|
if err := session.AddResource(resource); err != nil {
|
|
t.Fatalf("AddResource() failed: %v", err)
|
|
}
|
|
|
|
err = session.Close()
|
|
|
|
if !errors.Is(err, expectedErr) {
|
|
t.Fatalf("Close() error = %v, want %v",
|
|
err, expectedErr)
|
|
}
|
|
|
|
if !resource.closed {
|
|
t.Fatal("resource was not closed")
|
|
}
|
|
}
|
|
|
|
func TestSessionCloseNil(t *testing.T) {
|
|
var session *server.Session
|
|
|
|
if err := session.Close(); err != nil {
|
|
t.Fatalf("Close() on nil session failed: %v", err)
|
|
}
|
|
}
|