You're not actually passing parent to node by reference here. This means a value of parent is being copied to node in the test function. In this case, that value simply points to a Node object.
node.key = 1111 works as you expect because it uses that value to access the same object which parent is also pointing to. i.e. both node and parent contain values pointing to the same locations in memory. As such, both can observe the modification.
However when you say node = null, you're assigning a new value to the node variable inside the test function. This means you're changing the pointer stored as the value in that particular variable to null, which in no way modifies the value for parent - that's still pointing to the Node object.
Mind my amateur ASCII art, but I think it's kind of like this:
Test.test(parent);
<node object>
^ ^
. .
. .
. .
+------.----+ . +-----------+
| . | . |
| . | (-> copied to) | . |
| parent | | node |
+-----------+ +-----------+
Program.Main scope Test.test scope
node = null;
<node object>
^
.
.
.
+------.----+ +-----------+
| . | | |
| . | | |
| parent | | node=null |
+-----------+ +-----------+
Program.Main scope Test.test scope
Whereas if you happened to use public static void test(ref Node node) you could think of it more like this:
Test.test(parent);
<node object>
^
.
.
.
+------.----+ +-----------+
| parent <============================ node |
| | | |
| | | |
+-----------+ +-----------+
Program.Main scope Test.test scope
node = null;
<node object>
Lonely
+-----------+ +-----------+
| parent <============================ node |
| = | | |
| null | | |
+-----------+ +-----------+
Program.Main scope Test.test scope