diff --git a/src/ai/ai_client.c b/src/ai/ai_client.c index 41f07e45..aadc1e46 100644 --- a/src/ai/ai_client.c +++ b/src/ai/ai_client.c @@ -360,7 +360,7 @@ ai_list_providers(void) /* Stateful provider name finder with case-sensitive matching. * Maintains position in sorted list for deterministic tab-completion cycling. * The autocomplete is kept in sync via ai_add_provider/ai_remove_provider. */ -char* +gchar* ai_providers_find(const char* const search_str, gboolean previous, void* context) { /* Initialize autocomplete on first use */ @@ -368,8 +368,11 @@ ai_providers_find(const char* const search_str, gboolean previous, void* context providers_ac = autocomplete_new(); } + /* NULL search_str is treated as empty string for cycling */ + const char* effective_search = (search_str != NULL) ? search_str : ""; + /* Use stateful autocomplete */ - return autocomplete_complete(providers_ac, search_str, FALSE, previous); + return autocomplete_complete(providers_ac, effective_search, FALSE, previous); } gchar* diff --git a/src/ai/ai_client.h b/src/ai/ai_client.h index c6aaeca1..f61d6356 100644 --- a/src/ai/ai_client.h +++ b/src/ai/ai_client.h @@ -109,7 +109,7 @@ GList* ai_list_providers(void); * @param context Unused * @return Provider name, or NULL if not found */ -char* ai_providers_find(const char* const search_str, gboolean previous, void* context); +gchar* ai_providers_find(const char* const search_str, gboolean previous, void* context); /** * Get the API key for a provider. diff --git a/tests/unittests/test_ai_client.c b/tests/unittests/test_ai_client.c index d0a38cb6..ee00d316 100644 --- a/tests/unittests/test_ai_client.c +++ b/tests/unittests/test_ai_client.c @@ -356,20 +356,18 @@ void test_ai_providers_find_forward(void** state) { /* Test forward iteration - should return first match */ - char* result = ai_providers_find("o", FALSE, NULL); + auto_gchar gchar* result = ai_providers_find("o", FALSE, NULL); assert_non_null(result); assert_string_equal("openai", result); - g_free(result); } void test_ai_providers_find_forward_perplexity(void** state) { /* Test forward iteration for perplexity */ - char* result = ai_providers_find("p", FALSE, NULL); + auto_gchar gchar* result = ai_providers_find("p", FALSE, NULL); assert_non_null(result); assert_string_equal("perplexity", result); - g_free(result); } void @@ -378,17 +376,16 @@ test_ai_providers_find_forward_custom(void** state) /* Add a custom provider and test */ ai_add_provider("custom", "https://custom.api.com/v1", NULL); - char* result = ai_providers_find("c", FALSE, NULL); + auto_gchar gchar* result = ai_providers_find("c", FALSE, NULL); assert_non_null(result); assert_string_equal("custom", result); - g_free(result); } void test_ai_providers_find_forward_no_match(void** state) { /* Test no match */ - char* result = ai_providers_find("z", FALSE, NULL); + auto_gchar gchar* result = ai_providers_find("z", FALSE, NULL); assert_null(result); } @@ -396,26 +393,23 @@ void test_ai_providers_find_forward_partial_match(void** state) { /* Test partial match - should return providers starting with "ope" */ - char* result = ai_providers_find("ope", FALSE, NULL); + auto_gchar gchar* result = ai_providers_find("ope", FALSE, NULL); assert_non_null(result); assert_string_equal("openai", result); - g_free(result); } void test_ai_providers_find_next(void** state) { /* Test that stateless implementation returns same result each call */ - char* result1 = ai_providers_find("o", FALSE, NULL); + auto_gchar gchar* result1 = ai_providers_find("o", FALSE, NULL); assert_non_null(result1); assert_string_equal("openai", result1); - g_free(result1); /* Second call with same params returns same result (stateless) */ - char* result2 = ai_providers_find("o", FALSE, NULL); + auto_gchar gchar* result2 = ai_providers_find("o", FALSE, NULL); assert_non_null(result2); assert_string_equal("openai", result2); - g_free(result2); } void @@ -423,44 +417,47 @@ test_ai_providers_find_previous(void** state) { /* Test that previous=TRUE returns last match in list */ /* With only "openai" starting with "o", both FALSE and TRUE return same result */ - char* result1 = ai_providers_find("o", FALSE, NULL); + auto_gchar gchar* result1 = ai_providers_find("o", FALSE, NULL); assert_non_null(result1); assert_string_equal("openai", result1); - g_free(result1); /* previous=TRUE also returns "openai" (only one match, so first==last) */ - char* result2 = ai_providers_find("o", TRUE, NULL); + auto_gchar gchar* result2 = ai_providers_find("o", TRUE, NULL); assert_non_null(result2); assert_string_equal("openai", result2); - g_free(result2); } void test_ai_providers_find_null_search_str(void** state) { - char* result = ai_providers_find(NULL, FALSE, NULL); - assert_null(result); + /* NULL search_str triggers cycling: returns first provider in list */ + auto_gchar gchar* result = ai_providers_find(NULL, FALSE, NULL); + assert_non_null(result); + assert_string_equal("openai", result); } void test_ai_providers_find_empty_search_str(void** state) { - char* result = ai_providers_find("", FALSE, NULL); - assert_null(result); + /* Empty search_str triggers cycling: returns first provider in list */ + auto_gchar gchar* result = ai_providers_find("", FALSE, NULL); + assert_non_null(result); + assert_string_equal("openai", result); } void -test_ai_providers_find_case_sensitive(void** state) +test_ai_providers_find_case_insensitive(void** state) { - /* Test that matching is case-sensitive */ - char* result = ai_providers_find("OPENAI", FALSE, NULL); - assert_null(result); + /* Test that matching is case-insensitive (via g_ascii_strdown) */ + auto_gchar gchar* result = ai_providers_find("OPENAI", FALSE, NULL); + assert_non_null(result); + assert_string_equal("openai", result); result = ai_providers_find("OpenAI", FALSE, NULL); - assert_null(result); + assert_non_null(result); + assert_string_equal("openai", result); result = ai_providers_find("openai", FALSE, NULL); assert_non_null(result); assert_string_equal("openai", result); - g_free(result); } diff --git a/tests/unittests/test_ai_client.h b/tests/unittests/test_ai_client.h index d56d2e26..7593aad8 100644 --- a/tests/unittests/test_ai_client.h +++ b/tests/unittests/test_ai_client.h @@ -39,4 +39,4 @@ void test_ai_providers_find_next(void** state); void test_ai_providers_find_previous(void** state); void test_ai_providers_find_null_search_str(void** state); void test_ai_providers_find_empty_search_str(void** state); -void test_ai_providers_find_case_sensitive(void** state); +void test_ai_providers_find_case_insensitive(void** state); diff --git a/tests/unittests/unittests.c b/tests/unittests/unittests.c index 13698f95..f28b3724 100644 --- a/tests/unittests/unittests.c +++ b/tests/unittests/unittests.c @@ -687,7 +687,7 @@ main(int argc, char* argv[]) cmocka_unit_test_setup_teardown(test_ai_providers_find_previous, ai_client_setup, ai_client_teardown), cmocka_unit_test_setup_teardown(test_ai_providers_find_null_search_str, ai_client_setup, ai_client_teardown), cmocka_unit_test_setup_teardown(test_ai_providers_find_empty_search_str, ai_client_setup, ai_client_teardown), - cmocka_unit_test_setup_teardown(test_ai_providers_find_case_sensitive, ai_client_setup, ai_client_teardown), + cmocka_unit_test_setup_teardown(test_ai_providers_find_case_insensitive, ai_client_setup, ai_client_teardown), cmocka_unit_test(test_ai_json_escape), cmocka_unit_test(test_ai_json_escape_null), cmocka_unit_test(test_ai_json_escape_empty),