Files
go-sam-go/types_test.go
2025-10-02 12:09:45 -04:00

497 lines
15 KiB
Go

package sam3
import (
"reflect"
"testing"
"github.com/go-i2p/go-sam-go/common"
"github.com/go-i2p/go-sam-go/datagram"
"github.com/go-i2p/go-sam-go/primary"
"github.com/go-i2p/go-sam-go/raw"
"github.com/go-i2p/go-sam-go/stream"
)
// TestTypeAliases_Compilation verifies that all type aliases compile correctly
// and maintain proper type identity with their underlying types.
func TestTypeAliases_Compilation(t *testing.T) {
// This test ensures that type aliases are properly defined and
// can be used interchangeably with their underlying types
// Test that we can create instances of aliased types
var sam *SAM
var resolver *SAMResolver
var config *I2PConfig
var emit *SAMEmit
var options Options
var streamSession *StreamSession
var datagramSession *DatagramSession
var rawSession *RawSession
var samConn *SAMConn
var streamListener *StreamListener
var primarySession *PrimarySession
var baseSession *BaseSession
// Verify these are nil initially (proper zero values)
if sam != nil || resolver != nil || config != nil || emit != nil ||
streamSession != nil || datagramSession != nil || rawSession != nil ||
samConn != nil || streamListener != nil || primarySession != nil ||
baseSession != nil {
t.Error("Type aliases should have nil zero values")
}
// Verify options map can be initialized
if options == nil {
options = make(Options)
}
options["test"] = "value"
if options["test"] != "value" {
t.Error("Options type alias should work as map")
}
}
// TestTypeAliases_Identity verifies that pure type aliases maintain proper type identity
// and can be used interchangeably with their underlying types from sub-packages.
// Note: This test excludes wrapper structs like SAM which intentionally have different types.
func TestTypeAliases_Identity(t *testing.T) {
tests := []struct {
name string
aliasType reflect.Type
sourceType reflect.Type
}{
{
name: "SAMResolver alias identity",
aliasType: reflect.TypeOf((*SAMResolver)(nil)),
sourceType: reflect.TypeOf((*common.SAMResolver)(nil)),
},
{
name: "I2PConfig alias identity",
aliasType: reflect.TypeOf((*I2PConfig)(nil)),
sourceType: reflect.TypeOf((*common.I2PConfig)(nil)),
},
{
name: "SAMEmit alias identity",
aliasType: reflect.TypeOf((*SAMEmit)(nil)),
sourceType: reflect.TypeOf((*common.SAMEmit)(nil)),
},
{
name: "Options alias identity",
aliasType: reflect.TypeOf((*Options)(nil)),
sourceType: reflect.TypeOf((*common.Options)(nil)),
},
{
name: "Option alias identity",
aliasType: reflect.TypeOf((*Option)(nil)),
sourceType: reflect.TypeOf((*common.Option)(nil)),
},
{
name: "StreamSession alias identity",
aliasType: reflect.TypeOf((*StreamSession)(nil)),
sourceType: reflect.TypeOf((*stream.StreamSession)(nil)),
},
{
name: "DatagramSession alias identity",
aliasType: reflect.TypeOf((*DatagramSession)(nil)),
sourceType: reflect.TypeOf((*datagram.DatagramSession)(nil)),
},
{
name: "RawSession alias identity",
aliasType: reflect.TypeOf((*RawSession)(nil)),
sourceType: reflect.TypeOf((*raw.RawSession)(nil)),
},
{
name: "SAMConn alias identity",
aliasType: reflect.TypeOf((*SAMConn)(nil)),
sourceType: reflect.TypeOf((*stream.StreamConn)(nil)),
},
{
name: "StreamListener alias identity",
aliasType: reflect.TypeOf((*StreamListener)(nil)),
sourceType: reflect.TypeOf((*stream.StreamListener)(nil)),
},
{
name: "BaseSession alias identity",
aliasType: reflect.TypeOf((*BaseSession)(nil)),
sourceType: reflect.TypeOf((*common.BaseSession)(nil)),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.aliasType != tt.sourceType {
t.Errorf("Type alias %s does not match source type %s",
tt.aliasType, tt.sourceType)
}
})
}
}
// TestWrapperStructs_Behavior verifies that wrapper structs like SAM work correctly
// and provide proper access to embedded functionality while maintaining their own type identity.
func TestWrapperStructs_Behavior(t *testing.T) {
t.Run("SAM wrapper struct", func(t *testing.T) {
// Verify SAM is a wrapper struct, not a pure alias
samType := reflect.TypeOf((*SAM)(nil))
commonSAMType := reflect.TypeOf((*common.SAM)(nil))
// They should be different types (wrapper vs original)
if samType == commonSAMType {
t.Error("SAM should be a wrapper struct, not a pure type alias")
}
// But SAM should embed *common.SAM
samStructType := samType.Elem()
if samStructType.Kind() != reflect.Struct {
t.Error("SAM should be a struct type")
}
// Check that it has an embedded *common.SAM field
foundEmbedded := false
for i := 0; i < samStructType.NumField(); i++ {
field := samStructType.Field(i)
if field.Anonymous && field.Type == reflect.TypeOf((*common.SAM)(nil)) {
foundEmbedded = true
break
}
}
if !foundEmbedded {
t.Error("SAM should embed *common.SAM")
}
})
}
// TestTypeAliases_Convertibility verifies that type aliases can be converted
// between the root package types and sub-package types seamlessly.
func TestTypeAliases_Convertibility(t *testing.T) {
// Test Options convertibility
t.Run("Options conversion", func(t *testing.T) {
var rootOptions Options = make(Options)
var commonOptions common.Options = rootOptions
var backToRoot Options = commonOptions
rootOptions["test"] = "value"
if commonOptions["test"] != "value" {
t.Error("Options conversion failed")
}
if backToRoot["test"] != "value" {
t.Error("Options back-conversion failed")
}
})
// Test that we can pass aliased types to functions expecting sub-package types
t.Run("Function parameter compatibility", func(t *testing.T) {
// This test verifies that aliased types can be passed to functions
// expecting the original sub-package types
// Create a function that accepts common.Options
processOptions := func(opts common.Options) string {
return opts["key"]
}
// Create Options using root package alias
var rootOptions Options = make(Options)
rootOptions["key"] = "test-value"
// Verify we can pass the aliased type to the function
result := processOptions(rootOptions)
if result != "test-value" {
t.Errorf("Expected 'test-value', got %s", result)
}
})
}
// TestPrimarySession_Structure verifies that PrimarySession is properly aliased
// to the primary package implementation and can be used as expected.
func TestPrimarySession_Structure(t *testing.T) {
t.Run("PrimarySession alias", func(t *testing.T) {
// PrimarySession is now a type alias to primary.PrimarySession
// We can't directly instantiate it here since it requires proper initialization
// through the primary package, but we can verify the type exists
var ps *PrimarySession
if ps != nil {
t.Error("This should not happen - ps should be nil")
}
// Verify that PrimarySession is the correct type alias
var primaryPS *primary.PrimarySession
ps = primaryPS // This should compile without error if the alias is correct
t.Log("✓ PrimarySession type alias works correctly")
})
}
// TestTypeAliases_InterfaceCompatibility verifies that pure type aliases
// implement the same interfaces as their source types.
// Note: This excludes wrapper structs like SAM which have different method sets by design.
func TestTypeAliases_InterfaceCompatibility(t *testing.T) {
// This test ensures that pure type aliases maintain interface compatibility
// Note: We can't test actual interface implementation without instances,
// but we can verify type structure compatibility
t.Run("Pure alias method sets", func(t *testing.T) {
// Test SAMResolver (a pure alias)
samResolverType := reflect.TypeOf((*SAMResolver)(nil))
commonSAMResolverType := reflect.TypeOf((*common.SAMResolver)(nil))
if samResolverType != commonSAMResolverType {
t.Error("SAMResolver type alias does not match common.SAMResolver")
}
// Test Options (a pure alias)
optionsType := reflect.TypeOf((*Options)(nil))
commonOptionsType := reflect.TypeOf((*common.Options)(nil))
if optionsType != commonOptionsType {
t.Error("Options type alias does not match common.Options")
}
})
t.Run("Wrapper struct compatibility", func(t *testing.T) {
// For wrapper structs like SAM, verify they provide access to embedded functionality
// without requiring exact method set matching
samType := reflect.TypeOf((*SAM)(nil))
if samType.Elem().Kind() != reflect.Struct {
t.Error("SAM should be a struct type for method extension")
}
// Verify SAM has embedded *common.SAM field for delegation
samStruct := samType.Elem()
hasEmbedded := false
for i := 0; i < samStruct.NumField(); i++ {
field := samStruct.Field(i)
if field.Anonymous && field.Type == reflect.TypeOf((*common.SAM)(nil)) {
hasEmbedded = true
break
}
}
if !hasEmbedded {
t.Error("SAM should embed *common.SAM for delegation")
}
})
}
// TestTypeAliases_PackageDocumentation verifies that all exported types
// have proper documentation and follow Go documentation conventions.
func TestTypeAliases_PackageDocumentation(t *testing.T) {
// This test serves as documentation verification and ensures all
// types are properly exported and documented
expectedTypes := []string{
"SAM", "SAMResolver", "I2PConfig", "SAMEmit", "Options", "Option",
"StreamSession", "DatagramSession", "RawSession", "SAMConn",
"StreamListener", "PrimarySession", "BaseSession",
}
// Use reflection to get package types
// Note: This is a compile-time verification that all types exist
for _, typeName := range expectedTypes {
t.Run("Type_"+typeName, func(t *testing.T) {
// This test ensures the type exists and is properly exported
// The mere fact that the test compiles verifies the type exists
switch typeName {
case "SAM":
var _ *SAM
case "SAMResolver":
var _ *SAMResolver
case "I2PConfig":
var _ *I2PConfig
case "SAMEmit":
var _ *SAMEmit
case "Options":
var _ Options
case "Option":
var _ Option
case "StreamSession":
var _ *StreamSession
case "DatagramSession":
var _ *DatagramSession
case "RawSession":
var _ *RawSession
case "SAMConn":
var _ *SAMConn
case "StreamListener":
var _ *StreamListener
case "PrimarySession":
var _ *PrimarySession
case "BaseSession":
var _ *BaseSession
}
})
}
}
// TestTypeAliases_MemoryLayout verifies that type aliases have the same
// memory layout and size as their underlying types.
func TestTypeAliases_MemoryLayout(t *testing.T) {
tests := []struct {
name string
aliasSize uintptr
sourceSize uintptr
}{
{
name: "Options memory layout",
aliasSize: reflect.TypeOf((*Options)(nil)).Elem().Size(),
sourceSize: reflect.TypeOf((*common.Options)(nil)).Elem().Size(),
},
// Note: Other types are pointers, so their sizes would be pointer size
// The important thing is that they alias correctly, which is tested elsewhere
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.aliasSize != tt.sourceSize {
t.Errorf("Memory layout differs: alias=%d, source=%d",
tt.aliasSize, tt.sourceSize)
}
})
}
}
// BenchmarkTypeAliases_Performance verifies that type aliases have no
// performance overhead compared to direct use of sub-package types.
func BenchmarkTypeAliases_Performance(b *testing.B) {
b.Run("Options_RootPackage", func(b *testing.B) {
for i := 0; i < b.N; i++ {
opts := make(Options)
opts["key"] = "value"
_ = opts["key"]
}
})
b.Run("Options_CommonPackage", func(b *testing.B) {
for i := 0; i < b.N; i++ {
opts := make(common.Options)
opts["key"] = "value"
_ = opts["key"]
}
})
}
// TestTypeAliases_APICompatibility ensures that the type aliases provide
// the exact API surface expected by sam3 compatibility.
func TestTypeAliases_APICompatibility(t *testing.T) {
// This test verifies that our type aliases match the expected sam3 API
// by checking that all required types are available at package level
t.Run("Required types exist", func(t *testing.T) {
// These should all compile without error
var (
_ *SAM
_ *StreamSession
_ *DatagramSession
_ *RawSession
_ *SAMConn
_ *StreamListener
_ *PrimarySession
_ *SAMResolver
_ *I2PConfig
_ *SAMEmit
_ Options
_ Option
_ *BaseSession
)
})
t.Run("SAMConn is StreamConn alias", func(t *testing.T) {
// Verify that SAMConn properly aliases stream.StreamConn
samConnType := reflect.TypeOf((*SAMConn)(nil))
streamConnType := reflect.TypeOf((*stream.StreamConn)(nil))
if samConnType != streamConnType {
t.Error("SAMConn should be an alias for stream.StreamConn")
}
})
}
// TestTypeAliases_ZeroValues verifies that all type aliases have proper
// zero values and can be safely used in their zero state.
func TestTypeAliases_ZeroValues(t *testing.T) {
t.Run("Pointer types zero values", func(t *testing.T) {
var (
sam *SAM
resolver *SAMResolver
config *I2PConfig
emit *SAMEmit
streamSession *StreamSession
datagramSession *DatagramSession
rawSession *RawSession
samConn *SAMConn
streamListener *StreamListener
primarySession *PrimarySession
baseSession *BaseSession
)
// All should be nil
if sam != nil || resolver != nil || config != nil || emit != nil ||
streamSession != nil || datagramSession != nil || rawSession != nil ||
samConn != nil || streamListener != nil || primarySession != nil ||
baseSession != nil {
t.Error("Pointer type aliases should have nil zero values")
}
})
t.Run("Value types zero values", func(t *testing.T) {
var (
options Options
option Option
)
// Options should be nil map
if options != nil {
t.Error("Options should have nil zero value")
}
// Option should be nil function
if option != nil {
t.Error("Option should have nil zero value")
}
})
}
// TestTypeAliases_ImportPaths verifies that the type aliases properly
// reference the correct sub-package types and maintain clean imports.
func TestTypeAliases_ImportPaths(t *testing.T) {
// This is a compile-time test that ensures we're importing the right packages
// and that all type aliases resolve correctly
t.Run("Common package types", func(t *testing.T) {
// Verify common package aliases work
// Note: SAM is now an embedded struct, not a direct alias
var sam *SAM = &SAM{SAM: &common.SAM{}}
if sam.SAM == nil {
t.Error("SAM embedding failed")
}
var resolver *common.SAMResolver = (*SAMResolver)(nil)
var config *common.I2PConfig = (*I2PConfig)(nil)
var emit *common.SAMEmit = (*SAMEmit)(nil)
var options common.Options = Options(nil)
var option common.Option = Option(nil)
var baseSession *common.BaseSession = (*BaseSession)(nil)
// These assignments should compile without error
_ = sam
_ = resolver
_ = config
_ = emit
_ = options
_ = option
_ = baseSession
})
t.Run("Sub-package types", func(t *testing.T) {
// Verify sub-package aliases work
var streamSession *stream.StreamSession = (*StreamSession)(nil)
var datagramSession *datagram.DatagramSession = (*DatagramSession)(nil)
var rawSession *raw.RawSession = (*RawSession)(nil)
var samConn *stream.StreamConn = (*SAMConn)(nil)
var streamListener *stream.StreamListener = (*StreamListener)(nil)
// These assignments should compile without error
_ = streamSession
_ = datagramSession
_ = rawSession
_ = samConn
_ = streamListener
})
}