test(ai): autocomplete tests — 5 expected failures documenting real bugs

Adds 10 tests for ai_models_find and additional edge cases on
ai_providers_find. 5 pass as sanity; 5 fail intentionally, documenting
two distinct bugs in the autocomplete integration:

BUG A — ai_models_find cycling is broken
  src/ai/ai_client.c ai_models_find() allocates a fresh Autocomplete on
  every call and frees it before returning. The Autocomplete's last_found
  cursor is therefore never persisted, so repeated calls with the same
  prefix always return the first match. Tab+Tab+Tab cannot reach models
  2, 3, ...
  Caught by:
    - test_ai_models_find_cycles_through_matches
    - test_ai_models_find_null_search_cycles_all

BUG B — providers_ac is never reset between user input cycles
  src/ai/ai_client.c keeps the static providers_ac AC, but
  src/command/cmd_ac.c's all_acs[] (which cmd_ac_reset() iterates) does
  not include &providers_ac. So when the input loop fires the global
  reset, providers_ac retains its last_found cursor from the previous
  prefix.
  Result: tab-completing a new prefix continues from the old cursor
  position instead of restarting at the head of the new prefix's matches.
  A provider added mid-session may also be missed depending on cursor
  position.
  Caught by:
    - test_ai_providers_find_state_resets_on_prefix_change
    - test_ai_providers_find_empty_then_prefix
    - test_ai_providers_find_after_add_includes_new

Sanity (5 passing):
  - test_ai_models_find_no_models_returns_null
  - test_ai_models_find_returns_match_for_prefix
  - test_ai_models_find_no_match_returns_null
  - test_ai_models_find_previous_direction
  - test_ai_providers_find_after_remove_skips_removed

The failing tests are checked in as-is — they go green when the bugs
are fixed. See PR description for suggested fixes.
This commit is contained in:
2026-05-12 12:50:21 +03:00
committed by jabber.developer2
parent d676f3b087
commit befdd0ba10
3 changed files with 256 additions and 0 deletions

View File

@@ -775,6 +775,17 @@ main(int argc, char* argv[])
cmocka_unit_test_setup_teardown(test_ai_prefs_round_trip_api_key, ai_client_setup_with_prefs, ai_client_teardown_with_prefs),
cmocka_unit_test_setup_teardown(test_ai_prefs_round_trip_remove_key, ai_client_setup_with_prefs, ai_client_teardown_with_prefs),
cmocka_unit_test_setup_teardown(test_ai_prefs_multiple_providers_persist, ai_client_setup_with_prefs, ai_client_teardown_with_prefs),
/* Autocomplete deeper coverage */
cmocka_unit_test_setup_teardown(test_ai_models_find_no_models_returns_null, ai_client_setup, ai_client_teardown),
cmocka_unit_test_setup_teardown(test_ai_models_find_returns_match_for_prefix, ai_client_setup, ai_client_teardown),
cmocka_unit_test_setup_teardown(test_ai_models_find_no_match_returns_null, ai_client_setup, ai_client_teardown),
cmocka_unit_test_setup_teardown(test_ai_models_find_cycles_through_matches, ai_client_setup, ai_client_teardown),
cmocka_unit_test_setup_teardown(test_ai_models_find_previous_direction, ai_client_setup, ai_client_teardown),
cmocka_unit_test_setup_teardown(test_ai_models_find_null_search_cycles_all, ai_client_setup, ai_client_teardown),
cmocka_unit_test_setup_teardown(test_ai_providers_find_state_resets_on_prefix_change, ai_client_setup, ai_client_teardown),
cmocka_unit_test_setup_teardown(test_ai_providers_find_empty_then_prefix, ai_client_setup, ai_client_teardown),
cmocka_unit_test_setup_teardown(test_ai_providers_find_after_remove_skips_removed, ai_client_setup, ai_client_teardown),
cmocka_unit_test_setup_teardown(test_ai_providers_find_after_add_includes_new, ai_client_setup, ai_client_teardown),
// Flatfile export/import round-trip
cmocka_unit_test(test_ff_roundtrip_simple_chat),
cmocka_unit_test(test_ff_roundtrip_with_all_metadata),