Testing
Testing secured applications often requires mocking the user database so you don’t have to spin up a real database just to test authorization logic. To complement the core Rays testing framework, Rays Web Security provides a pkg/testing package containing in-memory implementations.
The InMemoryUserDetailsManager
The InMemoryUserDetailsManager allows you to rapidly configure a UserDetailsService populated with test users. It automatically encodes the plaintext passwords you provide using the injected PasswordEncoder.
These users are stored as InMemoryUser structs, which fully implement the UserDetails interface.
Test Configuration Example
Here is how you can define a test configuration that provisions an admin and a standard user for your test suites:
package tests
import (
. "github.com/BeamFoundry/rays-web-security/pkg/core"
. "github.com/BeamFoundry/rays-web-security/pkg/testing"
. "github.com/BeamFoundry/rays/pkg/core"
)
type UserDetailsServiceConfiguration struct {
Configuration
}
func (this *UserDetailsServiceConfiguration) UserDetailsService(
that struct {
passwordEncoder PasswordEncoder `@:"Inject"`
},
) UserDetailsService {
// Initialize the in-memory manager with the active password encoder
userDetailsService := InMemoryUserDetailsManager{}.New(that.passwordEncoder)
// Build a standard user
userDetailsService.NewUser().
WithUsername("user").
WithPassword("user").
WithAuthorities([]GrantedAuthority{Authority("ROLE_USER"), Authority("ROLE_PRIVILEGED")}).
Build()
// Build an admin user
userDetailsService.NewUser().
WithUsername("admin").
WithPassword("admin").
WithAuthorities([]GrantedAuthority{Authority("ROLE_USER"), Authority("ROLE_ADMIN")}).
Build()
return userDetailsService
}
var _, _ = any(&UserDetailsServiceConfiguration{}).(Stereotype)Integration with Ginkgo
You can easily load this mock configuration into your Rays TestingContainer during your Ginkgo test setup. Make sure to dot-import github.com/BeamFoundry/rays/pkg/testing to access GetContainer() and AddBeamFor(), which automatically hooks into Ginkgo to reset the container state between tests.
For maximum flexibility, initialize the container inside the It block rather than the BeforeEach block. This allows you to add specific Beams on a per-test basis before the container wires them up.
import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/BeamFoundry/rays/pkg/testing"
)
var _ = Describe("SecurityFilterChain", func() {
BeforeEach(func() {
// Register the mock user details service
AddBeamFor(&UserDetailsServiceConfiguration{})
GetContainer().EnableScanning()
})
It("should allow admin to access protected routes", func() {
// ... Add any test-specific mock Beams here ...
// Initialize the test container
err := GetContainer().Initialize()
// Ensure initialization occurred without error
Expect(err).NotTo(HaveOccurred())
// ... test logic ...
})
})