test: add concurrent fetch and read tests for privileges

Adds multiple tests to verify the thread-safety of the 
Fetch method and the handling of privileges in concurrent 
operations. Tests include concurrent fetching, reading 
privileges, and processing with multiple goroutines. 
Ensures no errors occur during operations and verifies 
privileges are set correctly.
This commit is contained in:
2025-11-03 12:40:04 +01:00
parent 05c59fa4a5
commit 4efc6572ee
2 changed files with 284 additions and 0 deletions
+1
View File
@@ -1,2 +1,3 @@
.idea
/release
coverage.txt
+283
View File
@@ -5,6 +5,7 @@ import (
"net/http"
"net/http/httptest"
"sort"
"sync"
"testing"
"github.com/sparetimecoders/goamqp"
@@ -332,3 +333,285 @@ func TestPrivilegeHandler_Fetch_Valid(t *testing.T) {
}
assert.Equal(t, expectedPrivileges, handler.privileges)
}
func TestPrivilegeHandler_Fetch_Concurrent_Fetches(t *testing.T) {
privileges := `
{
"jim@example.org": {
"00010203-0405-4607-8809-0a0b0c0d0e0f": {
"admin": false,
"company": true,
"consumer": false,
"time": true,
"invoicing": true,
"accounting": false,
"supplier": false,
"salary": true
}
}
}`
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(privileges))
}))
defer server.Close()
baseURL := server.Listener.Addr().String()
handler := New(WithBaseURL(fmt.Sprintf("http://%s", baseURL)))
// Run multiple Fetch calls concurrently to test thread-safety
var wg sync.WaitGroup
errors := make(chan error, 10)
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
if err := handler.Fetch(); err != nil {
errors <- err
}
}()
}
wg.Wait()
close(errors)
// Check no errors occurred
for err := range errors {
assert.NoError(t, err)
}
// Verify privileges were set correctly
expectedPrivileges := map[string]map[string]*CompanyPrivileges{
"jim@example.org": {
"00010203-0405-4607-8809-0a0b0c0d0e0f": {
Admin: false,
Company: true,
Consumer: false,
Time: true,
Invoicing: true,
Accounting: false,
Supplier: false,
Salary: true,
},
},
}
assert.Equal(t, expectedPrivileges, handler.privileges)
}
func TestPrivilegeHandler_Concurrent_Fetch_And_Read(t *testing.T) {
privileges := `
{
"jim@example.org": {
"abc-123": {
"admin": true,
"company": true,
"consumer": false,
"time": false,
"invoicing": false,
"accounting": false,
"supplier": false,
"salary": false
}
}
}`
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(privileges))
}))
defer server.Close()
baseURL := server.Listener.Addr().String()
handler := New(WithBaseURL(fmt.Sprintf("http://%s", baseURL)))
var wg sync.WaitGroup
errors := make(chan error, 100)
// Start multiple Fetch operations
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
if err := handler.Fetch(); err != nil {
errors <- err
}
}()
}
// Concurrently read privileges while Fetch is running
for i := 0; i < 50; i++ {
wg.Add(1)
go func() {
defer wg.Done()
_ = handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
return privileges.Admin
})
}()
}
// Concurrently check privileges while Fetch is running
for i := 0; i < 50; i++ {
wg.Add(1)
go func() {
defer wg.Done()
_ = handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
return privileges.Admin
})
}()
}
wg.Wait()
close(errors)
// Check no errors occurred
for err := range errors {
assert.NoError(t, err)
}
// Verify privileges are correct after all concurrent operations
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
return privileges.Admin
})
assert.Equal(t, []string{"abc-123"}, companies)
isAllowed := handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
return privileges.Admin && privileges.Company
})
assert.True(t, isAllowed)
}
func TestPrivilegeHandler_Concurrent_Process_And_Read(t *testing.T) {
handler := New(WithBaseURL("base"))
var wg sync.WaitGroup
// Concurrently add privileges via Process
for i := 0; i < 100; i++ {
wg.Add(1)
companyID := fmt.Sprintf("company-%d", i%10)
go func(id string) {
defer wg.Done()
_, _ = handler.Process(&PrivilegeAdded{
Email: "jim@example.org",
CompanyID: id,
Privilege: PrivilegeAdmin,
}, goamqp.Headers{})
}(companyID)
}
// Concurrently read privileges while Process is running
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
_ = handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
return privileges.Admin
})
}()
}
wg.Wait()
// Verify all companies were added
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
return privileges.Admin
})
sort.Strings(companies)
expected := make([]string, 10)
for i := 0; i < 10; i++ {
expected[i] = fmt.Sprintf("company-%d", i)
}
sort.Strings(expected)
assert.Equal(t, expected, companies)
}
func TestPrivilegeHandler_Concurrent_Multiple_Operations(t *testing.T) {
privileges := `
{
"jim@example.org": {
"initial-company": {
"admin": true,
"company": true,
"consumer": false,
"time": false,
"invoicing": false,
"accounting": false,
"supplier": false,
"salary": false
}
}
}`
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(privileges))
}))
defer server.Close()
baseURL := server.Listener.Addr().String()
handler := New(WithBaseURL(fmt.Sprintf("http://%s", baseURL)))
var wg sync.WaitGroup
// Fetch
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
_ = handler.Fetch()
}()
}
// Process PrivilegeAdded
for i := 0; i < 20; i++ {
wg.Add(1)
go func(idx int) {
defer wg.Done()
_, _ = handler.Process(&PrivilegeAdded{
Email: "jane@example.org",
CompanyID: fmt.Sprintf("company-%d", idx%5),
Privilege: PrivilegeCompany,
}, goamqp.Headers{})
}(i)
}
// CompaniesByUser reads
for i := 0; i < 50; i++ {
wg.Add(1)
email := "jim@example.org"
if i%2 == 0 {
email = "jane@example.org"
}
go func(e string) {
defer wg.Done()
_ = handler.CompaniesByUser(e, func(privileges CompanyPrivileges) bool {
return privileges.Admin || privileges.Company
})
}(email)
}
// IsAllowed reads
for i := 0; i < 50; i++ {
wg.Add(1)
go func() {
defer wg.Done()
_ = handler.IsAllowed("jim@example.org", "initial-company", func(privileges CompanyPrivileges) bool {
return privileges.Admin
})
}()
}
wg.Wait()
// Verify final state is consistent
jimCompanies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
return privileges.Admin
})
assert.Contains(t, jimCompanies, "initial-company")
janeCompanies := handler.CompaniesByUser("jane@example.org", func(privileges CompanyPrivileges) bool {
return privileges.Company
})
sort.Strings(janeCompanies)
expectedJane := []string{"company-0", "company-1", "company-2", "company-3", "company-4"}
assert.Equal(t, expectedJane, janeCompanies)
}