@@ -33,27 +33,50 @@ Describe "Qdrant Member Leave Bash Script Tests"
3333 End
3434 End
3535
36- Describe " qdrant_select_target_peer_id ()"
36+ Describe " qdrant_select_target_peer_id_for_shard ()"
3737 cluster_info=' {"result":{"peers":{"1":{"uri":"http://qdrant-0:6335/"},"2":{"uri":"http://qdrant-1:6335/"},"3":{"uri":"http://qdrant-2:6335/"}}}}'
3838
39- It " keeps the raft leader as the shard target when the leader is not leaving"
40- When call qdrant_select_target_peer_id " $cluster_info " " 1" " 2"
39+ It " uses the raft leader when it does not already own the shard"
40+ collection_info=' {"result":{"peer_id":3,"local_shards":[{"shard_id":8}],"remote_shards":[{"shard_id":7,"peer_id":1}],"shard_transfers":[]}}'
41+ When call qdrant_select_target_peer_id_for_shard " $cluster_info " " $collection_info " " 1" " 2" " 7"
4142 The status should be success
4243 The output should eq " 2"
4344 End
4445
45- It " selects a surviving peer when the leaving peer is the raft leader"
46- When call qdrant_select_target_peer_id " $cluster_info " " 1" " 1"
46+ It " skips a leader that owns the shard as a remote replica"
47+ collection_info=' {"result":{"peer_id":3,"local_shards":[{"shard_id":8}],"remote_shards":[{"shard_id":7,"peer_id":1},{"shard_id":7,"peer_id":2}],"shard_transfers":[]}}'
48+ When call qdrant_select_target_peer_id_for_shard " $cluster_info " " $collection_info " " 1" " 2" " 7"
4749 The status should be success
48- The output should eq " 2"
50+ The output should eq " 3"
51+ End
52+
53+ It " skips a leader that owns the shard locally"
54+ collection_info=' {"result":{"peer_id":2,"local_shards":[{"shard_id":7}],"remote_shards":[{"shard_id":7,"peer_id":1}],"shard_transfers":[]}}'
55+ When call qdrant_select_target_peer_id_for_shard " $cluster_info " " $collection_info " " 1" " 2" " 7"
56+ The status should be success
57+ The output should eq " 3"
58+ End
59+
60+ It " skips an in-flight transfer destination for the shard"
61+ collection_info=' {"result":{"peer_id":3,"local_shards":[{"shard_id":8}],"remote_shards":[{"shard_id":7,"peer_id":1}],"shard_transfers":[{"shard_id":7,"from":3,"to":2}]}}'
62+ When call qdrant_select_target_peer_id_for_shard " $cluster_info " " $collection_info " " 1" " 2" " 7"
63+ The status should be success
64+ The output should eq " 3"
4965 End
5066
51- It " returns empty when there is no surviving peer"
52- single_peer_cluster =' {"result":{"peers":{"1":{"uri":"http://qdrant-0:6335/"}} }}'
53- When call qdrant_select_target_peer_id " $single_peer_cluster " " 1 " " 1"
67+ It " returns empty when every surviving peer already owns the shard "
68+ collection_info =' {"result":{"peer_id":2,"local_shards":[{"shard_id":7}],"remote_shards":[{"shard_id":7,"peer_id":1},{"shard_id":7,"peer_id":3}],"shard_transfers":[] }}'
69+ When call qdrant_select_target_peer_id_for_shard " $cluster_info " " $collection_info " " 1" " 2 " " 7 "
5470 The status should be success
5571 The output should eq " "
5672 End
73+
74+ It " fails closed when a local shard has no serving peer identity"
75+ collection_info=' {"result":{"local_shards":[{"shard_id":7}],"remote_shards":[{"shard_id":7,"peer_id":1}],"shard_transfers":[]}}'
76+ When call qdrant_select_target_peer_id_for_shard " $cluster_info " " $collection_info " " 1" " 2" " 7"
77+ The status should be failure
78+ The stderr should include " local shard without peer_id"
79+ End
5780 End
5881
5982 Describe " memberLeave action-wide deadline"
@@ -138,7 +161,8 @@ Describe "Qdrant Member Leave Bash Script Tests"
138161 control_uri=" http://control"
139162 leave_peer_uri=" http://leaving"
140163 leave_peer_id=" 1"
141- target_peer_id=" 2"
164+ leader_peer_id=" 2"
165+ cluster_info=' {"result":{"peers":{"1":{"uri":"http://qdrant-0:6335/"},"2":{"uri":"http://qdrant-1:6335/"},"3":{"uri":"http://qdrant-2:6335/"}}}}'
142166 qdrant_member_leave_deadline=$(( SECONDS + 50 ))
143167 }
144168 Before " setup_replay_state"
@@ -177,6 +201,31 @@ Describe "Qdrant Member Leave Bash Script Tests"
177201 The output should include " already moving after failed submit"
178202 End
179203
204+ It " submits the move to an unused peer instead of an existing replica"
205+ replicated_info=' {"result":{"peer_id":3,"local_shards":[{"shard_id":8}],"remote_shards":[{"shard_id":7,"peer_id":1},{"shard_id":7,"peer_id":2}],"shard_transfers":[]}}'
206+ qdrant_curl () {
207+ printf ' %s\n' " $* "
208+ }
209+
210+ When call qdrant_submit_shard_move_if_needed " demo" " 7" " $replicated_info "
211+ The status should be success
212+ The output should include ' "to_peer_id":3'
213+ The output should not include ' "to_peer_id":2'
214+ End
215+
216+ It " does not submit a move when every surviving peer already owns the shard"
217+ replicated_info=' {"result":{"peer_id":2,"local_shards":[{"shard_id":7}],"remote_shards":[{"shard_id":7,"peer_id":1},{"shard_id":7,"peer_id":3}],"shard_transfers":[]}}'
218+ qdrant_curl () {
219+ echo " unexpected qdrant_curl call" >&2
220+ return 99
221+ }
222+
223+ When call qdrant_submit_shard_move_if_needed " demo" " 7" " $replicated_info "
224+ The status should be failure
225+ The stderr should include " refusing to reduce replica count"
226+ The stderr should not include " unexpected qdrant_curl call"
227+ End
228+
180229 It " discovers remaining shards from the control endpoint when the leaving endpoint is unavailable"
181230 qdrant_curl () {
182231 last_arg=" ${*: -1} "
0 commit comments