@@ -171,7 +171,6 @@ func acceptConsentHandler(t *testing.T, c *client.Client, adminClient *hydra.API
171171func TestAuthCodeWithDefaultStrategy (t * testing.T ) {
172172 t .Parallel ()
173173
174- rng := rand .New (rand .NewSource (time .Now ().UnixNano ()))
175174 ctx := context .Background ()
176175
177176 for dbName , reg := range testhelpers .ConnectDatabases (t , true , driver .WithConfigOptions (configx .WithValues (map [string ]any {
@@ -181,6 +180,8 @@ func TestAuthCodeWithDefaultStrategy(t *testing.T) {
181180 t .Run ("registry=" + dbName , func (t * testing.T ) {
182181 t .Parallel ()
183182
183+ rng := rand .New (rand .NewSource (time .Now ().UnixNano ()))
184+
184185 jwk .EnsureAsymmetricKeypairExists (t , reg , string (jose .ES256 ), x .OpenIDConnectKeyName )
185186 jwk .EnsureAsymmetricKeypairExists (t , reg , string (jose .ES256 ), x .OAuth2JWTKeyName )
186187
@@ -1451,7 +1452,7 @@ func TestAuthCodeWithDefaultStrategy(t *testing.T) {
14511452 // - In the first scenario, all token generations are created at the same time.
14521453 // - In the second scenario, we create token generations with a delay that is longer than the grace period between them.
14531454 //
1454- // Tokens for each generation are created in parallel to ensure we have no state leak anywhere.0
1455+ // Tokens for each generation are created in parallel to ensure we have no state leak anywhere.
14551456 t .Run ("token generations" , func (t * testing.T ) {
14561457 gracePeriod := time .Second
14571458 aboveGracePeriod := 2 * time .Second
@@ -1465,12 +1466,22 @@ func TestAuthCodeWithDefaultStrategy(t *testing.T) {
14651466 generations [0 ] = []* oauth2.Token {issueTokens (t )}
14661467 // Start from the first generation. For every next generation, we refresh all the tokens of the previous generation twice.
14671468 for i := range len (generations ) - 1 {
1469+ // Loop invariants:
1470+ // - `generations` is constant is size (it is right-sized when created), thus it is safe to index it concurrently.
1471+ // - The current generation (`generations[i]`) is constant in size, thus it is safe to iterate over it.
1472+ // - The next generation (`generations[i+1]`) is *not* constant in size. Elements are appended to it concurrently, and thus it is guarded by `mtx`.
1473+ // - Elements of the current generation *are* modified in `refreshToken` (!), and thus are guarded by `mtx`.
1474+ // - Elements of the current and next generation are concurrently read/written inside the `gen` function, and thus are guarded by `mtx`.
14681475 generations [i + 1 ] = make ([]* oauth2.Token , 0 , len (generations [i ])* 2 )
1476+ mtx := sync.Mutex {}
14691477
14701478 var wg sync.WaitGroup
14711479 gen := func (token * oauth2.Token ) {
14721480 defer wg .Done ()
1481+
1482+ mtx .Lock ()
14731483 generations [i + 1 ] = append (generations [i + 1 ], refreshTokens (t , token ))
1484+ mtx .Unlock ()
14741485 }
14751486
14761487 for _ , token := range generations [i ] {
0 commit comments