hook_init() will always be triggered at the beginning of the request, as you noticed.
When you say "... redirects the user based on various user rules." what do you mean? Is the user redirected when submitting the form with some content? Is the user redirected when visiting (or dialing) specific URLs? What conditions exist? Is this a rule module ?
Depending on which user rules you have in mind, there will be different answers. For example, if the rules have something to do with nodes (loading, viewing, editing, ...), you should use hook_node_$op() , where $op can be, for example, “view” or “download” or “send " As an example:
// Redirect user when submitting a node of type 'book' function mymodule_node_submit($node, $form, &$form_state) { if ($node->type === 'book') { drupal_goto("some/place/else"); } }
Edit
There are two problems here that I see. One of them is that in the code to host the redirection logic, which in Drupal 7 (and 6) really does not have a good short answer, it all depends on the context. This may be in response to loading a form or submitting a form or loading or viewing a node or viewing or editing (et.c.) or a number of other conditions. These conditions require that the redirection logic be in different places in your code. This is what I focused on, answering above.
The second problem, which, reading your explanations, seems to be the main culprit, has a different type. The Cron and unit tests will not have all the information that a regular web browser visitor has visited.
You will need to determine if cron is working and not being redirected when this happens. As you can see in the link above, cron creates a temporary (anonymous) user and does not save session data. This tends to break many runs. Because drupal_exit () more or less kills everything .
As for unit testing drupal_goto() , I'm less sure here, but I think it breaks for the same reason. You can try to make fun of part of the function so that you don’t actually redirect.
And as a note, you might consider using hook_url_inbound_alter () ( more ). This may or may not be compatible with what you want to do ... See what the Redirect module does , especially the redirect_can_redirect() function.