query_packages now assigns @search_sources - a hash mapping package ids to the matching strategy that put them into the results: :exact, :name_prefix, :name_contains, :semantic or :fulltext. The grid and list views render a small badge with a humanized label per result, making the search tiers observable. semantic_results() was folded into search_packages() so each branch tags its own source. Adds the rails-controller-testing gem for assigns() in tests.
189 lines
7.2 KiB
Ruby
189 lines
7.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'test_helper'
|
|
|
|
class PackagesControllerTest < ActionController::TestCase
|
|
test 'should get index and contain dummy package-4' do
|
|
get :grid
|
|
assert_response :success
|
|
assert_select 'div', 'package-4'
|
|
end
|
|
|
|
test 'should get list view with_screenshots and find Firefox' do
|
|
# package = Package.create!(name: 'firefox', description: 'A web browser.')
|
|
# Screenshot.create!(package: package, approved: true, hidden: false)
|
|
|
|
# get :list, params: { show: 'with' }
|
|
# assert_response :success
|
|
# assert_select 'div', 'A web browser.'
|
|
|
|
get :list, params: { show: 'with' }
|
|
assert_response :success
|
|
assert_select 'div', 'A program to browse web sites.'
|
|
end
|
|
|
|
test 'exact package name search wins without calling the vector service' do
|
|
with_stubbed_method(Package, :nearest_to_text,
|
|
->(_text) { flunk 'should not call vector search' }) do
|
|
get :grid, params: { search: 'vim' }
|
|
assert_response :success
|
|
assert_select 'div', 'vim'
|
|
assert_equal({ packages(:vim).id => :exact }, assigns(:search_sources))
|
|
assert_select '.search-source .label', 'exact match'
|
|
end
|
|
end
|
|
|
|
test 'gibberish search shows no results and never calls the vector service' do
|
|
with_stubbed_method(Package, :nearest_to_text,
|
|
->(_text) { flunk 'should not call vector search' }) do
|
|
get :grid, params: { search: 'asdfgh zzzz' }
|
|
assert_response :success
|
|
# the grid renders every package in the database when no search
|
|
# filter applies - so an empty result must not fall back to that
|
|
assert_select 'div', text: /package-\d+/, count: 0
|
|
assert_empty assigns(:search_sources)
|
|
end
|
|
end
|
|
|
|
test 'name prefix matches rank above semantic results' do
|
|
with_stubbed_method(Package, :nearest_to_text,
|
|
->(_text, **_opts) { Package.where(name: packages(:vim).name) }) do
|
|
get :grid, params: { search: 'package' }
|
|
assert_response :success
|
|
# fixtures create package-0..package-99 - prefix hits must render
|
|
assert_select 'div', 'package-0'
|
|
# ...and before any semantically found package
|
|
assert response.body.index('package-0') < response.body.index('vim')
|
|
sources = assigns(:search_sources)
|
|
assert_equal :name_prefix, sources[Package.find_by(name: 'package-0').id]
|
|
assert_equal :semantic, sources[packages(:vim).id]
|
|
end
|
|
end
|
|
|
|
test 'prefix matches and semantic results are deduplicated' do
|
|
with_stubbed_method(Package, :nearest_to_text,
|
|
->(_text, **_opts) { Package.where(name: packages(:firefox).name) }) do
|
|
get :grid, params: { search: 'fire' }
|
|
assert_response :success
|
|
assert_select 'div', text: 'firefox', count: 1
|
|
# the higher tier wins the attribution
|
|
assert_equal :name_prefix, assigns(:search_sources)[packages(:firefox).id]
|
|
end
|
|
end
|
|
|
|
test 'compound words in search match packages with joined names' do
|
|
# "fire fox" should find "firefox" via name substring matching
|
|
with_stubbed_method(Package, :nearest_to_text,
|
|
->(_text, **_opts) { Package.none }) do
|
|
get :grid, params: { search: 'fire fox' }
|
|
assert_response :success
|
|
assert_select 'div', text: 'firefox', count: 1
|
|
assert_equal({ packages(:firefox).id => :name_contains },
|
|
assigns(:search_sources))
|
|
assert_select '.search-source .label', 'name match'
|
|
end
|
|
end
|
|
|
|
test 'details shows semantically related packages' do
|
|
packages(:vim).update_column(:embedding, Array.new(384, 0.25))
|
|
packages(:firefox).update_column(:embedding, Array.new(384, 0.75))
|
|
|
|
get :details, params: { name: 'vim' }
|
|
assert_response :success
|
|
assert_select '#related .pkgname', text: 'firefox'
|
|
end
|
|
|
|
test 'details without embedding renders no related section' do
|
|
get :details, params: { name: 'vim' }
|
|
assert_response :success
|
|
assert_select '#related', count: 0
|
|
end
|
|
|
|
test 'search uses semantic (vector) results' do
|
|
relation = Package.where(name: packages(:vim).name)
|
|
with_stubbed_method(Package, :nearest_to_text, ->(_text, **_opts) { relation }) do
|
|
get :grid, params: { search: 'editor' }
|
|
assert_response :success
|
|
assert_select 'div', 'vim'
|
|
assert_equal({ packages(:vim).id => :semantic }, assigns(:search_sources))
|
|
end
|
|
end
|
|
|
|
# There is deliberately no fallback for an empty vector search
|
|
# result: KNN always returns something as long as packages have
|
|
# embeddings. Full-text search is only used when the vector service
|
|
# cannot be reached.
|
|
|
|
test 'search falls back to full-text search when vector service fails' do
|
|
with_stubbed_method(Vectorizer, :embed,
|
|
->(_text) { raise Vectorizer::Error, 'service down' }) do
|
|
get :grid, params: { search: 'editor' }
|
|
assert_response :success
|
|
assert_select 'div', 'vim'
|
|
assert flash[:error].present?
|
|
assert_equal({ packages(:vim).id => :fulltext }, assigns(:search_sources))
|
|
end
|
|
end
|
|
|
|
# test 'should get thumbnail for a package and a desired version' do
|
|
# # get '/thumbnail-with-version/package-5/0.1'
|
|
# get thumbnail_with_version_url('package-5', '0.1')
|
|
# end
|
|
|
|
# test 'should get without_screenshots' do
|
|
# get :without_screenshots
|
|
# assert_response :success
|
|
# end
|
|
|
|
# test "should get moderate" do
|
|
# get :moderate
|
|
# assert_response :success
|
|
# end
|
|
|
|
# test "upload screenshot anonymously then login and get screenshots assigned" do
|
|
# newest_screenshot1 = Package.find_by_name(:firefox).screenshots.order(id: :desc).first
|
|
|
|
# # Load the upload page
|
|
# get :upload, params: { name: 'firefox' }
|
|
# assert_response :success
|
|
|
|
# # Upload a screenshot and expect to be redirected afterwards
|
|
# post :upload_receive, params: { name: 'firefox', file: [ fixture_file_upload('test/fixtures/files/large1.png','image/png') ] }
|
|
# assert_redirected_to package_path(name: 'firefox')
|
|
|
|
# # Check that the newest screenshot is not yet approved
|
|
# newest_screenshot2 = Package.find_by_name(:firefox).screenshots.order(id: :desc).first
|
|
# assert_not_equal newest_screenshot1, newest_screenshot2
|
|
# assert_equal newest_screenshot2.approved, false
|
|
|
|
# # Login
|
|
# sign_in users(:normal)
|
|
|
|
# # Get the information about the newest screenshot again
|
|
# newest_screenshot2 = Package.find_by_name(:firefox).screenshots.order(id: :desc).first
|
|
|
|
# # Expect the uploaded screenshot to be assigned to this user
|
|
# byebug
|
|
# assert_equal newest_screenshot2.user, users(:normal)
|
|
|
|
# # (name: 'firefox')
|
|
# # img_count_before = page.find_all('img').count
|
|
# # attach_file 'file[]', Rails.root.join('test/fixtures/files/large1.png')
|
|
# # click_on 'Start upload'
|
|
|
|
# # # The cookie session must remember this anonymous upload
|
|
# # newest_screenshot = Package.find_by_name(:firefox).screenshots.order(id: :desc).first
|
|
# # assert_equal session[:uploaded_screenshots].length, [newest_screenshot]
|
|
|
|
# # #
|
|
# # # sign_in users(:debian)
|
|
|
|
# # # # Screenshot is instantly public
|
|
# # # newest_screenshot = Package.find_by_name(:firefox).screenshots.order(id: :desc).first
|
|
# # # assert_equal newest_screenshot.approved, true
|
|
|
|
# # # # Clean up
|
|
# # newest_screenshot.destroy
|
|
# # sign_out users(:normal)
|
|
# end
|
|
end
|