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:
@@ -1,2 +1,3 @@
|
||||
.idea
|
||||
/release
|
||||
coverage.txt
|
||||
|
||||
+283
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user