Maybe something with reverb/pusher and a presence channel?
Make sure each user gets a unique ID for their instance of a test, issue that same ID if the test already exists (eg, they simply refresh the page). Broadcast that Id on the presence channel, and refuse to start a new instance of the test if the presence channel already contains the test ID.
Snapey's presence channel idea is solid, but if you want to avoid the overhead of Reverb/websockets, you can handle this reliably using a Redis cache lock combined with a JS heartbeat. I've used this exact pattern in production for high-stakes exam platforms.
Backend Lock: In that controller, store the lock in Redis: Cache::put("test_lock_{$test->id}user{$user->id}", $tab_id, 10);. The 10-second TTL is the magic number here.
Enforcement: On the actual test attempt route (and on subsequent test submissions), check this cache key. If the key exists AND the value doesn't match the current request's tab_id, throw your "already active" error.