Skip to content

Commit

Permalink
Move rest of skipped controller tests to JobTest
Browse files Browse the repository at this point in the history
- Move skipped controller methods (leaving them skipped)
- Delete unused controller test utility methods
  • Loading branch information
JoeCohen committed Aug 24, 2024
1 parent ec109f0 commit 08a8b10
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 356 deletions.
356 changes: 0 additions & 356 deletions test/controllers/observations/inat_imports_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ def test_import_authorized
inat_import.save
inat_authorization_callback_params = { code: "MockCode" }

# stub_token_requests
login(user.login)

assert_difference(
Expand All @@ -150,253 +149,8 @@ def test_import_authorized
assert_redirected_to(observations_path)
end

def test_import_lycoperdon
# TODO: Move to InatImportJobTest
skip("To be moved to InatImportJobTest")
obs = import_mock_observation("lycoperdon")

assert(obs.images.any?, "Obs should have images")
assert(obs.sequences.one?, "Obs should have a sequence")
end

# Prove that Namings, Votes, Identification are correct
# When iNat obs has provisional name that's in MO
def test_import_arrhenia_sp_ny02_old_name
# TODO: Move to InatImportJobTest
skip("To be moved to InatImportJobTest")
name = Name.create(
text_name: 'Arrhenia "sp-NY02"',
author: "S.D. Russell crypt. temp.",
display_name: '**__Arrhenia "sp-NY02"__** S.D. Russell crypt. temp.',
rank: "Species",
user: rolf
)

obs = import_mock_observation("arrhenia_sp_NY02")

namings = obs.namings
naming = namings.find_by(name: name)
assert(naming.present?, "Missing Naming for provisional name")
assert_equal(inat_manager, naming.user,
"Naming without iNat ID should have user: inat_manager")
vote = Vote.find_by(naming: naming, user: naming.user)
assert(vote.present?, "Naming is missing a Vote")
assert_equal(name, obs.name, "Consensus ID should be provisional name")
assert(vote.value.positive?, "Vote for MO consensus should be positive")
end

# Prove that Namings, Votes, Identification are correct
# when iNat obs has provisional name that wasn't in MO
def test_import_arrhenia_sp_ny02_new_name
# TODO: Move to InatImportJobTest
skip("To be moved to InatImportJobTest")
assert_nil(Name.find_by(text_name: 'Arrhenia "sp-NY02"'),
"Test requires that MO not yest have provisional name")

obs = import_mock_observation("arrhenia_sp_NY02")

name = Name.find_by(text_name: 'Arrhenia "sp-NY02"')
assert(name.rss_log_id.present?)

assert(name.present?, "Failed to create provisional name")
namings = obs.namings
naming = namings.find_by(name: name)
assert(naming.present?, "Missing Naming for provisional name")
assert_equal(inat_manager, naming.user,
"Naming without iNat ID should have user: inat_manager")
vote = Vote.find_by(naming: naming, user: naming.user)
assert(vote.present?, "Naming is missing a Vote")
assert_equal(name, obs.name, "Consensus ID should be provisional name")
assert(vote.value.positive?, "Vote for MO consensus should be positive")
end

def test_import_plant
# TODO: Move to InatImportJobTest
skip("To be moved to InatImportJobTest")
user = rolf
filename = "ceanothus_cordulatus"
mock_search_result = File.read("test/inat/#{filename}.txt")
inat_import_ids = InatObs.new(
JSON.generate(JSON.parse(mock_search_result)["results"].first)
).inat_id

simulate_all_inat_interactions(user: user,
inat_import_ids: inat_import_ids,
mock_inat_response: mock_search_result)

params = { inat_ids: inat_import_ids, code: "MockCode" }
login(user.login)

assert_no_difference(
"Observation.count", "Should not import iNat Plant observations"
) do
post(:authorization_response, params: params)
end

assert_flash_text(:inat_taxon_not_importable.l(id: inat_import_ids))
end

def test_import_zero_results
# TODO: Move to InatImportJobTest
skip("To be moved to InatImportJobTest")
user = rolf
filename = "zero_results"
mock_search_result = File.read("test/inat/#{filename}.txt")
inat_import_ids = "123"

simulate_all_inat_interactions(
user: user, inat_import_ids: inat_import_ids,
mock_inat_response: mock_search_result
)

params = { inat_ids: inat_import_ids, code: "MockCode" }
login(user.login)

assert_no_difference(
"Observation.count",
"Should not import if there's no iNat obs with a matching id"
) do
post(:authorization_response, params: params)
end
end

def test_import_multiple
# TODO: Move to InatImportJobTest
skip("To be moved to InatImportJobTest")
# NOTE: using obss without photos to avoid stubbing photo import
# amanita_flavorubens, calostoma lutescens
inat_obss = "231104466,195434438"
inat_import_ids = inat_obss
user = users(:rolf)
filename = "listed_ids"
mock_inat_response = File.read("test/inat/#{filename}.txt")
# prove that mock was constructed properly
json = JSON.parse(mock_inat_response)
assert_equal(2, json["total_results"])
assert_equal(1, json["page"])
assert_equal(30, json["per_page"])
# mock is sorted by id, asc
assert_equal(195_434_438, json["results"].first["id"])
assert_equal(231_104_466, json["results"].second["id"])

simulate_all_inat_interactions(
user: user, inat_import_ids: inat_import_ids,
mock_inat_response: mock_inat_response
)

params = { inat_ids: inat_import_ids, code: "MockCode" }
login(user.login)

assert_difference(
"Observation.count", 2, "Failed to create multiple observations"
) do
post(:authorization_response, params: params)
end
end

def test_import_all
# TODO: Move to InatImportJobTest
skip("To be moved to InatImportJobTest")
user = users(:rolf)
login(user.login)

filename = "import_all"
mock_search_result = File.read("test/inat/#{filename}.txt")
# shorten it to one page to avoid stubbing multiple inat api requests
mock_search_result = limited_to_first_page(mock_search_result)
# delete the photos in order to avoid stubbing photo imports
mock_search_result = result_without_photos(mock_search_result)

inat_import_ids = ""

simulate_all_inat_interactions(
user: user, inat_import_ids: inat_import_ids,
mock_inat_response: mock_search_result,
import_all: true
)

params = { inat_ids: inat_import_ids, code: "MockCode" }

assert_difference(
"Observation.count", 2, "Failed to create multiple observations"
) do
post(:authorization_response, params: params)
end
end

########## Utilities

def inat_manager
User.find_by(login: "MO Webmaster")
end

def import_mock_observation(filename)
user = users(:rolf)
mock_search_result = File.read("test/inat/#{filename}.txt")
inat_obs = InatObs.new(
JSON.generate(
JSON.parse(mock_search_result)["results"].first
)
)
inat_import_ids = inat_obs.inat_id

simulate_all_inat_interactions(
user: user, inat_username: inat_obs.inat_user_login,
inat_import_ids: inat_import_ids,
mock_inat_response: mock_search_result
)

params = { inat_ids: inat_import_ids, code: "MockCode" }
login(user.login)

# NOTE: Stubs the importer's return value, but not its side-effect --
# i.e., doesn't add Image(s) to the MO Observation.
# Enables testing everything except Observation.images. jdc 2024-06-23
InatPhotoImporter.stub(:new, mock_photo_importer(inat_obs)) do
assert_difference("Observation.count", 1, "Failed to create Obs") do
post(:authorization_response, params: params)
end
end

Observation.order(created_at: :asc).last
end

def simulate_all_inat_interactions(
mock_inat_response:, user: rolf, inat_username: nil, inat_import_ids: "",
import_all: false, id_above: 0
)
simulate_inat_accredications(
user: user, inat_username: inat_username,
inat_import_ids: inat_import_ids, import_all: import_all
)
stub_inat_api_request(inat_import_ids, mock_inat_response,
id_above: id_above,
inat_user_login: inat_username)
end

def simulate_inat_accredications(
user: rolf, inat_username: nil, inat_import_ids: "", import_all: false
)
simulate_authorization(
user: user, inat_username: inat_username,
inat_import_ids: inat_import_ids, import_all: import_all
)
stub_token_requests
end

def simulate_authorization(
user: rolf, inat_username: nil, inat_import_ids: "", import_all: false
)
inat_import = InatImport.find_or_create_by(user: user)
inat_import.import_all = import_all
# ignore list of ids if importing all a user's iNat obss
inat_import.inat_ids = import_all == true ? "" : inat_import_ids
inat_import.state = "Authorizing"
inat_import.inat_username = inat_username
inat_import.save
stub_request(:any, authorization_url)
end

# iNat url where user is sent in order to authorize MO access
# to iNat confidential data
# https://www.inaturalist.org/pages/api+reference#authorization_code_flow
Expand All @@ -406,115 +160,5 @@ def authorization_url
"&redirect_uri=#{REDIRECT_URI}" \
"&response_type=code"
end

def stub_token_requests
stub_oauth_token_request
# must trade oauth access token for a JWT in order to use iNat API v1
stub_jwt_request
end

# stub exchanging iNat code for oauth token
# https://www.inaturalist.org/pages/api+reference#authorization_code_flow
def stub_oauth_token_request
stub_request(:post, "#{SITE}/oauth/token").
with(
body: { "client_id" => Rails.application.credentials.inat.id,
"client_secret" => Rails.application.credentials.inat.secret,
"code" => "MockCode",
"grant_type" => "authorization_code",
"redirect_uri" => REDIRECT_URI }
).
to_return(status: 200,
body: { access_token: "MockAccessToken" }.to_json,
headers: {})
end

def stub_jwt_request
stub_request(:get, "#{SITE}/users/api_token").
with(
headers: {
"Accept" => "application/json",
"Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"Authorization" => "Bearer MockAccessToken",
"Host" => "www.inaturalist.org"
}
).
to_return(status: 200,
body: { access_token: "MockJWT" }.to_json,
headers: {})
end

def stub_inat_api_request(inat_obs_ids, mock_inat_response, id_above: 0,
inat_user_login: nil)
# params must be in same order as in the controller
# omit trailing "=" since the controller omits it (via `merge`)
params = <<~PARAMS.delete("\n").chomp("=")
?iconic_taxa=#{Observations::InatImportsController::ICONIC_TAXA}
&id=#{inat_obs_ids}
&id_above=#{id_above}
&per_page=200
&only_id=false
&order=asc&order_by=id
&user_login=#{inat_user_login}
PARAMS
stub_request(:get, "#{API_BASE}/observations#{params}").
with(headers:
{ "Accept" => "application/json",
"Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"Authorization" => "Bearer",
"Host" => "api.inaturalist.org" }).
to_return(body: mock_inat_response)
end

def mock_photo_importer(inat_obs)
mock_inat_photo = inat_obs.inat_obs_photos.first
mock_image_api = MockImageAPI.new(
results: [
expected_mo_image(mock_inat_photo: mock_inat_photo,
user: User.current)
]
)
mock_photo_importer = Minitest::Mock.new
inat_obs.inat_obs_photos.length.times do
mock_photo_importer.expect(:api, mock_image_api)
end
mock_photo_importer
end

def expected_mo_image(mock_inat_photo:, user:)
Image.create(
content_type: "image/jpeg",
user_id: user.id,
notes: "Imported from iNat " \
"#{DateTime.now.utc.strftime("%Y-%m-%d %H:%M:%S %z")}",
copyright_holder: mock_inat_photo[:photo][:attribution],
license_id: expected_mo_photo_license(mock_inat_photo),
width: 2048,
height: 1534,
original_name: "iNat photo uuid #{mock_inat_photo[:uuid]}"
)
end

def expected_mo_photo_license(mock_inat_photo)
InatLicense.new(mock_inat_photo[:photo][:license_code]).
mo_license.id
end

def result_without_photos(mock_search_result)
ms_hash = JSON.parse(mock_search_result)
ms_hash["results"].each do |result|
result["observation_photos"] = []
result["photos"] = []
end
JSON.generate(ms_hash)
end

# Turn results with many pages into results with one page
# By ignoring all pages but the first
def limited_to_first_page(mock_search_result)
ms_hash = JSON.parse(mock_search_result)
ms_hash["total_results"] = ms_hash["results"].length
JSON.generate(ms_hash)
end
end
end
Loading

0 comments on commit 08a8b10

Please sign in to comment.