Related to this question, I have a functional test asserting that hook_node_access is doing what it should for different users.
While I can access edit and delete routes, I add an additional check for $node->access() and see what its result is:
$low_user->set('field_suppliers', $supplier->id());
$low_user->save();
// With the supplier set, now we should have access.
$this->drupalGet('/node/' . $offer->id() . '/edit');
$this->assertSession()->statusCodeEquals(200);
$this->drupalGet('/node/' . $offer->id() . '/delete');
$this->assertSession()->statusCodeEquals(200);
// Why is this required?
$offer->save();
$this->assertSame(TRUE, $offer->access('update', $low_user));
$this->assertSame(TRUE, $offer->access('delete', $low_user));
Everything passes except the last two statements unless I save the node. I assume this is due to the line:
AccessResult::allowedIf(!empty($user_has_suppliers_with_node))->cachePerUser()->addCacheableDependency($node);
I also tried using resetCache and invalidateTags against the node, neither worked. Only saving the node will the test pass - but this is not required in implementation.
Edit:
Swapping $offer->save()
with \Drupal::service('plugin.cache_clearer')->clearCachedDefinitions();
also works.
I suppose I should be simulating an admin logging in and updating that user account and hitting "Save" on the user form, but I also had trouble getting the edit array to do anything for an autocomplete field. That is the difference between what is implemented and how its being tested (user action vs code).
AccessResult::allowed()->cachePerPermissions()
.cachePerPermissions()
adds the user.permissions cache context.