mirror of
https://github.com/go-i2p/go-sam-go.git
synced 2025-06-15 21:28:46 -04:00
add tests for raw sessions
This commit is contained in:
238
raw/listen_test.go
Normal file
238
raw/listen_test.go
Normal file
@ -0,0 +1,238 @@
|
||||
package raw
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-i2p/go-sam-go/common"
|
||||
)
|
||||
|
||||
func TestRawSession_Listen(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setupSession func() *RawSession
|
||||
wantErr bool
|
||||
errContains string
|
||||
}{
|
||||
{
|
||||
name: "successful_listen",
|
||||
setupSession: func() *RawSession {
|
||||
// Create a mock SAM connection
|
||||
sam := &common.SAM{}
|
||||
baseSession := &common.BaseSession{}
|
||||
return &RawSession{
|
||||
BaseSession: baseSession,
|
||||
sam: sam,
|
||||
options: []string{},
|
||||
closed: false,
|
||||
}
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "listen_on_closed_session",
|
||||
setupSession: func() *RawSession {
|
||||
sam := &common.SAM{}
|
||||
baseSession := &common.BaseSession{}
|
||||
return &RawSession{
|
||||
BaseSession: baseSession,
|
||||
sam: sam,
|
||||
options: []string{},
|
||||
closed: true,
|
||||
}
|
||||
},
|
||||
wantErr: true,
|
||||
errContains: "session is closed",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
session := tt.setupSession()
|
||||
|
||||
listener, err := session.Listen()
|
||||
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("Listen() expected error but got none")
|
||||
return
|
||||
}
|
||||
if tt.errContains != "" && !containsString(err.Error(), tt.errContains) {
|
||||
t.Errorf("Listen() error = %v, want error containing %q", err, tt.errContains)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Listen() unexpected error = %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if listener == nil {
|
||||
t.Error("Listen() returned nil listener")
|
||||
return
|
||||
}
|
||||
|
||||
// Verify listener properties
|
||||
if listener.session != session {
|
||||
t.Error("Listener session reference incorrect")
|
||||
}
|
||||
|
||||
if listener.reader == nil {
|
||||
t.Error("Listener reader not initialized")
|
||||
}
|
||||
|
||||
if listener.acceptChan == nil {
|
||||
t.Error("Listener acceptChan not initialized")
|
||||
}
|
||||
|
||||
if listener.errorChan == nil {
|
||||
t.Error("Listener errorChan not initialized")
|
||||
}
|
||||
|
||||
if listener.closeChan == nil {
|
||||
t.Error("Listener closeChan not initialized")
|
||||
}
|
||||
|
||||
// Clean up
|
||||
if listener != nil {
|
||||
_ = listener.Close()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawListener_Properties(t *testing.T) {
|
||||
// Setup a basic session for testing
|
||||
sam := &common.SAM{}
|
||||
baseSession := &common.BaseSession{}
|
||||
session := &RawSession{
|
||||
BaseSession: baseSession,
|
||||
sam: sam,
|
||||
options: []string{},
|
||||
closed: false,
|
||||
}
|
||||
|
||||
listener, err := session.Listen()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create listener: %v", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
t.Run("channels_buffered_correctly", func(t *testing.T) {
|
||||
// Check that acceptChan has proper buffer size
|
||||
select {
|
||||
case listener.acceptChan <- &RawConn{}:
|
||||
// Should not block for first 10 items
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Error("acceptChan appears to be unbuffered or too small")
|
||||
}
|
||||
|
||||
// Drain the channel
|
||||
select {
|
||||
case <-listener.acceptChan:
|
||||
default:
|
||||
t.Error("Failed to read from acceptChan")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("initial_state", func(t *testing.T) {
|
||||
if listener.closed {
|
||||
t.Error("New listener should not be closed initially")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestRawListener_Close(t *testing.T) {
|
||||
sam := &common.SAM{}
|
||||
baseSession := &common.BaseSession{}
|
||||
session := &RawSession{
|
||||
BaseSession: baseSession,
|
||||
sam: sam,
|
||||
options: []string{},
|
||||
closed: false,
|
||||
}
|
||||
|
||||
listener, err := session.Listen()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create listener: %v", err)
|
||||
}
|
||||
|
||||
// Test closing
|
||||
err = listener.Close()
|
||||
if err != nil {
|
||||
t.Errorf("Close() returned error: %v", err)
|
||||
}
|
||||
|
||||
// Verify closed state
|
||||
listener.mu.RLock()
|
||||
closed := listener.closed
|
||||
listener.mu.RUnlock()
|
||||
|
||||
if !closed {
|
||||
t.Error("Listener should be marked as closed after Close()")
|
||||
}
|
||||
|
||||
// Test double close
|
||||
err = listener.Close()
|
||||
if err == nil {
|
||||
t.Error("Second Close() should return error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawListener_Concurrent_Access(t *testing.T) {
|
||||
sam := &common.SAM{}
|
||||
baseSession := &common.BaseSession{}
|
||||
session := &RawSession{
|
||||
BaseSession: baseSession,
|
||||
sam: sam,
|
||||
options: []string{},
|
||||
closed: false,
|
||||
}
|
||||
|
||||
listener, err := session.Listen()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create listener: %v", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
// Test concurrent access to listener state
|
||||
done := make(chan bool, 2)
|
||||
|
||||
go func() {
|
||||
defer func() { done <- true }()
|
||||
for i := 0; i < 100; i++ {
|
||||
listener.mu.RLock()
|
||||
_ = listener.closed
|
||||
listener.mu.RUnlock()
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer func() { done <- true }()
|
||||
for i := 0; i < 100; i++ {
|
||||
listener.mu.RLock()
|
||||
_ = listener.session
|
||||
listener.mu.RUnlock()
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for both goroutines
|
||||
<-done
|
||||
<-done
|
||||
}
|
||||
|
||||
// Helper function to check if a string contains a substring
|
||||
func containsString(s, substr string) bool {
|
||||
return len(s) >= len(substr) &&
|
||||
(substr == "" || findString(s, substr))
|
||||
}
|
||||
|
||||
func findString(s, substr string) bool {
|
||||
for i := 0; i <= len(s)-len(substr); i++ {
|
||||
if s[i:i+len(substr)] == substr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
274
raw/session_test.go
Normal file
274
raw/session_test.go
Normal file
@ -0,0 +1,274 @@
|
||||
package raw
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-i2p/go-sam-go/common"
|
||||
"github.com/go-i2p/i2pkeys"
|
||||
)
|
||||
|
||||
const testSAMAddr = "127.0.0.1:7656"
|
||||
|
||||
func setupTestSAM(t *testing.T) (*common.SAM, i2pkeys.I2PKeys) {
|
||||
t.Helper()
|
||||
|
||||
sam, err := common.NewSAM(testSAMAddr)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create SAM connection: %v", err)
|
||||
}
|
||||
|
||||
keys, err := sam.NewKeys()
|
||||
if err != nil {
|
||||
sam.Close()
|
||||
t.Fatalf("Failed to generate keys: %v", err)
|
||||
}
|
||||
|
||||
return sam, keys
|
||||
}
|
||||
|
||||
func TestNewRawSession(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
id string
|
||||
options []string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "basic session creation",
|
||||
id: "test_raw_session",
|
||||
options: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "session with options",
|
||||
id: "test_raw_with_opts",
|
||||
options: []string{"inbound.length=1", "outbound.length=1"},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "session with small tunnel config",
|
||||
id: "test_raw_small",
|
||||
options: []string{
|
||||
"inbound.length=0",
|
||||
"outbound.length=0",
|
||||
"inbound.lengthVariance=0",
|
||||
"outbound.lengthVariance=0",
|
||||
"inbound.quantity=1",
|
||||
"outbound.quantity=1",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sam, keys := setupTestSAM(t)
|
||||
defer sam.Close()
|
||||
|
||||
session, err := NewRawSession(sam, tt.id, keys, tt.options)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("NewRawSession() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
// Verify session properties
|
||||
if session.ID() != tt.id {
|
||||
t.Errorf("Session ID = %v, want %v", session.ID(), tt.id)
|
||||
}
|
||||
|
||||
if session.Keys().Addr().Base32() != keys.Addr().Base32() {
|
||||
t.Error("Session keys don't match provided keys")
|
||||
}
|
||||
|
||||
addr := session.Addr()
|
||||
if addr.Base32() == "" {
|
||||
t.Error("Session address is empty")
|
||||
}
|
||||
|
||||
// Clean up
|
||||
if err := session.Close(); err != nil {
|
||||
t.Errorf("Failed to close session: %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawSession_Close(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
sam, keys := setupTestSAM(t)
|
||||
defer sam.Close()
|
||||
|
||||
session, err := NewRawSession(sam, "test_close", keys, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create session: %v", err)
|
||||
}
|
||||
|
||||
// Close the session
|
||||
err = session.Close()
|
||||
if err != nil {
|
||||
t.Errorf("Close() error = %v", err)
|
||||
}
|
||||
|
||||
// Closing again should not error
|
||||
err = session.Close()
|
||||
if err != nil {
|
||||
t.Errorf("Second Close() error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawSession_Addr(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
sam, keys := setupTestSAM(t)
|
||||
defer sam.Close()
|
||||
|
||||
session, err := NewRawSession(sam, "test_addr", keys, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create session: %v", err)
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
addr := session.Addr()
|
||||
expectedAddr := keys.Addr()
|
||||
|
||||
if addr.Base32() != expectedAddr.Base32() {
|
||||
t.Errorf("Addr() = %v, want %v", addr.Base32(), expectedAddr.Base32())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawSession_NewReader(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
sam, keys := setupTestSAM(t)
|
||||
defer sam.Close()
|
||||
|
||||
session, err := NewRawSession(sam, "test_reader", keys, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create session: %v", err)
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
reader := session.NewReader()
|
||||
if reader == nil {
|
||||
t.Error("NewReader() returned nil")
|
||||
}
|
||||
|
||||
if reader.session != session {
|
||||
t.Error("Reader session reference is incorrect")
|
||||
}
|
||||
|
||||
// Verify channels are initialized
|
||||
if reader.recvChan == nil {
|
||||
t.Error("Reader recvChan is nil")
|
||||
}
|
||||
if reader.errorChan == nil {
|
||||
t.Error("Reader errorChan is nil")
|
||||
}
|
||||
if reader.closeChan == nil {
|
||||
t.Error("Reader closeChan is nil")
|
||||
}
|
||||
if reader.doneChan == nil {
|
||||
t.Error("Reader doneChan is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawSession_NewWriter(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
sam, keys := setupTestSAM(t)
|
||||
defer sam.Close()
|
||||
|
||||
session, err := NewRawSession(sam, "test_writer", keys, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create session: %v", err)
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
writer := session.NewWriter()
|
||||
if writer == nil {
|
||||
t.Error("NewWriter() returned nil")
|
||||
}
|
||||
|
||||
if writer.session != session {
|
||||
t.Error("Writer session reference is incorrect")
|
||||
}
|
||||
|
||||
if writer.timeout != 30 {
|
||||
t.Errorf("Writer timeout = %v, want 30", writer.timeout)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawSession_PacketConn(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
sam, keys := setupTestSAM(t)
|
||||
defer sam.Close()
|
||||
|
||||
session, err := NewRawSession(sam, "test_packetconn", keys, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create session: %v", err)
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
conn := session.PacketConn()
|
||||
if conn == nil {
|
||||
t.Error("PacketConn() returned nil")
|
||||
}
|
||||
|
||||
rawConn, ok := conn.(*RawConn)
|
||||
if !ok {
|
||||
t.Error("PacketConn() did not return a RawConn")
|
||||
}
|
||||
|
||||
if rawConn.session != session {
|
||||
t.Error("RawConn session reference is incorrect")
|
||||
}
|
||||
|
||||
if rawConn.reader == nil {
|
||||
t.Error("RawConn reader is nil")
|
||||
}
|
||||
|
||||
if rawConn.writer == nil {
|
||||
t.Error("RawConn writer is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawAddr_Network(t *testing.T) {
|
||||
addr := &RawAddr{}
|
||||
if addr.Network() != "i2p-raw" {
|
||||
t.Errorf("Network() = %v, want i2p-raw", addr.Network())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawAddr_String(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
sam, keys := setupTestSAM(t)
|
||||
defer sam.Close()
|
||||
|
||||
addr := &RawAddr{addr: keys.Addr()}
|
||||
expected := keys.Addr().Base32()
|
||||
|
||||
if addr.String() != expected {
|
||||
t.Errorf("String() = %v, want %v", addr.String(), expected)
|
||||
}
|
||||
}
|
13
raw/types_test.go
Normal file
13
raw/types_test.go
Normal file
@ -0,0 +1,13 @@
|
||||
package raw
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/go-i2p/go-sam-go/common"
|
||||
)
|
||||
|
||||
var (
|
||||
ds common.Session = &RawSession{}
|
||||
dl net.Listener = &RawListener{}
|
||||
dc net.PacketConn = &RawConn{}
|
||||
)
|
Reference in New Issue
Block a user