# frozen_string_literal: true require 'test_helper' class PackageTest < ActiveSupport::TestCase test 'embedding_text combines description and long description' do package = packages(:firefox) assert_equal "A web browser\nA program to browse web sites.", package.embedding_text end test 'embedding_text works with missing long description' do package = packages(:firefox) package.long_description = nil assert_equal 'A web browser', package.embedding_text end test 'update_embedding! stores the vector returned by the vector service' do vector = Array.new(384) { |i| i / 384.0 } with_stubbed_method(Vectorizer, :embed, ->(_text) { vector }) do package = packages(:vim) assert package.update_embedding! # The vector type stores float32 so allow small rounding differences package.reload.embedding.to_a.each_with_index do |actual, i| assert_in_delta vector[i], actual, 1e-6 end end end test 'update_embedding! does nothing without description text' do package = packages(:firefox) package.description = nil package.long_description = nil with_stubbed_method(Vectorizer, :embed, ->(_text) { flunk 'should not call the vector service' }) do assert_not package.update_embedding! end end test 'nearest_to_text returns none for blank text' do assert_empty Package.nearest_to_text('') assert_empty Package.nearest_to_text(nil) end test 'name_starts_with matches name prefixes case-insensitively' do assert_includes Package.name_starts_with('fir').map(&:name), packages(:firefox).name assert_not_includes Package.name_starts_with('vim').map(&:name), packages(:firefox).name assert_empty Package.name_starts_with('') assert_empty Package.name_starts_with(nil) end test 'name_contains_all finds compound words split by spaces' do assert_includes Package.name_contains_all(%w[fire fox]).map(&:name), packages(:firefox).name # all tokens must be present in the name assert_empty Package.name_contains_all(%w[firefox zzzz]) assert_empty Package.name_contains_all([]) # single character tokens would flood results - ignore them assert_empty Package.name_contains_all(['f']) end test 'lexical_overlap? pins the english dictionary regardless of server config' do # Regression: with default_text_search_config = german (as on some # servers) 'browser' stems to 'brows' which never matches the # english-stemmed index. connection = ActiveRecord::Base.connection original = connection.select_value('SHOW default_text_search_config') connection.execute("SET default_text_search_config = 'german'") assert Package.lexical_overlap?('browser') ensure connection&.execute("SET default_text_search_config = '#{original}'") end test 'lexical_overlap? detects real words vs gibberish' do # words present in fixture package descriptions assert Package.lexical_overlap?('browser') assert Package.lexical_overlap?('email client for vim syntax') assert Package.lexical_overlap?('the browser') # stopwords are ignored # gibberish, numbers and empty strings share no vocabulary assert_not Package.lexical_overlap?('asdfgh zzzz') assert_not Package.lexical_overlap?('123 !!!') assert_not Package.lexical_overlap?('the') # stopword-only query assert_not Package.lexical_overlap?('') assert_not Package.lexical_overlap?(nil) end test 'nearest_to_text embeds the query and searches' do vector = Array.new(384, 0.25) embed_called_with = nil with_stubbed_method(Vectorizer, :embed, lambda { |text| embed_called_with = text vector }) do received = [] results = with_stubbed_method(Package, :nearest_neighbors, lambda { |_field, received_vector, **_opts| received << received_vector [packages(:vim)] }) do Package.nearest_to_text('some search text') end assert_equal [packages(:vim)], results assert_equal 'some search text', embed_called_with assert_equal [vector], received end end end