@@ -160,3 +160,219 @@ test_that("calculatePeptideBindingLoad accepts character vector of alleles", {
160160 expect_type(result , " double" )
161161 expect_true(result > = 0 )
162162})
163+
164+ # --- Identical Genotype Tests ---
165+
166+ test_that(" calculatePeptideBindingLoad returns 0 for identical genotypes with no mismatched peptides" , {
167+ # When donor and recipient have identical alleles, no mismatched peptides should be derived
168+ recipient <- hlaGeno(data.frame (A_1 = " A*02:01" , A_2 = " A*03:01" , stringsAsFactors = FALSE ))
169+ donor <- hlaGeno(data.frame (A_1 = " A*02:01" , A_2 = " A*03:01" , stringsAsFactors = FALSE ))
170+
171+ # With empty peptides (when peptides cannot be derived), should return 0
172+ result <- calculatePeptideBindingLoad(recipient , character (0 ), return = " total" )
173+ expect_equal(result , 0 )
174+ })
175+
176+ # --- PWM Backend Detailed Tests ---
177+
178+ test_that(" PWM backend produces consistent scores for known binders" , {
179+ recipient <- hlaGeno(data.frame (A_1 = " A*02:01" , stringsAsFactors = FALSE ))
180+ # GILGFVFTL is a well-known A*02:01 binder (influenza M1 peptide)
181+ peptides <- c(" GILGFVFTL" )
182+
183+ result <- calculatePeptideBindingLoad(recipient , peptides , backend = " pwm" , return = " detail" )
184+
185+ expect_equal(nrow(result ), 1 )
186+ expect_equal(result $ peptide , " GILGFVFTL" )
187+ expect_equal(result $ hla_allele , " A*02:01" )
188+ expect_true(result $ predicted_ic50 > 0 )
189+ })
190+
191+ test_that(" PWM backend handles unknown supertypes gracefully" , {
192+ # Use an allele that might not be in the supertype mapping
193+ recipient <- hlaGeno(data.frame (A_1 = " A*99:01" , stringsAsFactors = FALSE ))
194+ peptides <- c(" GILGFVFTL" )
195+
196+ # Should not error, falls back to A02-like
197+ result <- calculatePeptideBindingLoad(recipient , peptides , backend = " pwm" , return = " detail" )
198+ expect_equal(nrow(result ), 1 )
199+ })
200+
201+ # --- Summary Return Tests ---
202+
203+ test_that(" summary return correctly aggregates across alleles" , {
204+ recipient <- hlaGeno(data.frame (
205+ A_1 = " A*02:01" , A_2 = " A*03:01" ,
206+ stringsAsFactors = FALSE
207+ ))
208+ peptides <- c(" GILGFVFTL" , " NLVPMVATV" , " FLKEKGGL" )
209+
210+ result <- calculatePeptideBindingLoad(recipient , peptides , return = " summary" )
211+
212+ expect_equal(nrow(result ), 2 ) # Two alleles
213+ expect_true(all(result $ n_strong > = 0 ))
214+ expect_true(all(result $ n_weak > = 0 ))
215+ expect_true(all(result $ n_strong + result $ n_weak < = result $ n_peptides ))
216+ })
217+
218+ test_that(" summary totals match detail breakdown" , {
219+ recipient <- hlaGeno(data.frame (A_1 = " A*02:01" , stringsAsFactors = FALSE ))
220+ peptides <- c(" GILGFVFTL" , " NLVPMVATV" , " FLKEKGGL" )
221+
222+ detail <- calculatePeptideBindingLoad(recipient , peptides , return = " detail" )
223+ summary <- calculatePeptideBindingLoad(recipient , peptides , return = " summary" )
224+
225+ expect_equal(summary $ n_peptides [1 ], nrow(detail ))
226+ expect_equal(summary $ n_strong [1 ], sum(detail $ binding_level == " strong" ))
227+ expect_equal(summary $ n_weak [1 ], sum(detail $ binding_level == " weak" ))
228+ })
229+
230+ # --- Contribution Calculation Tests ---
231+
232+ test_that(" contribution scores are non-negative" , {
233+ recipient <- hlaGeno(data.frame (A_1 = " A*02:01" , stringsAsFactors = FALSE ))
234+ peptides <- c(" GILGFVFTL" , " NLVPMVATV" )
235+
236+ result <- calculatePeptideBindingLoad(recipient , peptides , return = " detail" )
237+
238+ expect_true(all(result $ contribution > = 0 ))
239+ })
240+
241+ test_that(" strong binders have higher contribution than weak binders" , {
242+ recipient <- hlaGeno(data.frame (A_1 = " A*02:01" , stringsAsFactors = FALSE ))
243+ peptides <- c(" GILGFVFTL" , " NLVPMVATV" , " FLKEKGGL" , " AAAAAAAAL" )
244+
245+ result <- calculatePeptideBindingLoad(
246+ recipient , peptides ,
247+ binding_threshold = 500 ,
248+ weak_threshold = 5000 ,
249+ return = " detail"
250+ )
251+
252+ # Check that multiplier difference is reflected
253+ strong <- result [result $ binding_level == " strong" , ]
254+ weak <- result [result $ binding_level == " weak" , ]
255+
256+ # This tests the formula: strong gets 2x multiplier, weak gets 1x
257+ # For same IC50, strong contribution should be ~2x weak
258+ if (nrow(strong ) > 0 && nrow(weak ) > 0 ) {
259+ # At least verify strong binders have non-zero contribution
260+ expect_true(all(strong $ contribution > 0 ))
261+ }
262+ })
263+
264+ # --- Edge Case Tests ---
265+
266+ test_that(" calculatePeptideBindingLoad handles single character peptide input" , {
267+ recipient <- hlaGeno(data.frame (A_1 = " A*02:01" , stringsAsFactors = FALSE ))
268+
269+ # Single short peptide (will be filtered out for 9-mer requirement)
270+ result <- calculatePeptideBindingLoad(recipient , " ABC" , peptide_length = 9L )
271+ expect_equal(result , 0 )
272+ })
273+
274+ test_that(" calculatePeptideBindingLoad handles peptides with non-standard characters" , {
275+ recipient <- hlaGeno(data.frame (A_1 = " A*02:01" , stringsAsFactors = FALSE ))
276+ # Peptide with X (unknown)
277+ peptides <- c(" GILGFVFTX" )
278+
279+ result <- calculatePeptideBindingLoad(recipient , peptides , return = " detail" )
280+ expect_equal(nrow(result ), 1 ) # Should still process
281+ })
282+
283+ test_that(" calculatePeptideBindingLoad handles duplicate peptides" , {
284+ recipient <- hlaGeno(data.frame (A_1 = " A*02:01" , stringsAsFactors = FALSE ))
285+ peptides <- c(" GILGFVFTL" , " GILGFVFTL" , " GILGFVFTL" )
286+
287+ result <- calculatePeptideBindingLoad(recipient , peptides , return = " detail" )
288+
289+ # Duplicates should be deduplicated internally by .getPeptides
290+ expect_equal(nrow(result ), 1 )
291+ })
292+
293+ # --- Different Peptide Lengths ---
294+
295+ test_that(" calculatePeptideBindingLoad works with different peptide lengths" , {
296+ recipient <- hlaGeno(data.frame (A_1 = " A*02:01" , stringsAsFactors = FALSE ))
297+
298+ # 8-mers
299+ peptides_8 <- c(" GILGFVFT" , " NLVPMVAT" )
300+ result_8 <- calculatePeptideBindingLoad(recipient , peptides_8 , peptide_length = 8L , return = " detail" )
301+ expect_equal(nrow(result_8 ), 2 )
302+ expect_true(all(nchar(result_8 $ peptide ) == 8 ))
303+
304+ # 10-mers
305+ peptides_10 <- c(" GILGFVFTLA" , " NLVPMVATVA" )
306+ result_10 <- calculatePeptideBindingLoad(recipient , peptides_10 , peptide_length = 10L , return = " detail" )
307+ expect_equal(nrow(result_10 ), 2 )
308+ expect_true(all(nchar(result_10 $ peptide ) == 10 ))
309+ })
310+
311+ # --- Aggregate Method Tests ---
312+
313+ test_that(" aggregate_method sum gives total of all contributions" , {
314+ recipient <- hlaGeno(data.frame (A_1 = " A*02:01" , stringsAsFactors = FALSE ))
315+ peptides <- c(" GILGFVFTL" , " NLVPMVATV" )
316+
317+ detail <- calculatePeptideBindingLoad(recipient , peptides , return = " detail" )
318+ total_sum <- calculatePeptideBindingLoad(recipient , peptides , aggregate_method = " sum" )
319+
320+ expect_equal(total_sum , sum(detail $ contribution ))
321+ })
322+
323+ test_that(" aggregate_method max gives maximum contribution" , {
324+ recipient <- hlaGeno(data.frame (A_1 = " A*02:01" , stringsAsFactors = FALSE ))
325+ peptides <- c(" GILGFVFTL" , " NLVPMVATV" )
326+
327+ detail <- calculatePeptideBindingLoad(recipient , peptides , return = " detail" )
328+ total_max <- calculatePeptideBindingLoad(recipient , peptides , aggregate_method = " max" )
329+
330+ expect_equal(total_max , max(detail $ contribution ))
331+ })
332+
333+ test_that(" aggregate_method mean gives average contribution" , {
334+ recipient <- hlaGeno(data.frame (A_1 = " A*02:01" , stringsAsFactors = FALSE ))
335+ peptides <- c(" GILGFVFTL" , " NLVPMVATV" )
336+
337+ detail <- calculatePeptideBindingLoad(recipient , peptides , return = " detail" )
338+ total_mean <- calculatePeptideBindingLoad(recipient , peptides , aggregate_method = " mean" )
339+
340+ expect_equal(total_mean , mean(detail $ contribution ))
341+ })
342+
343+ # --- Class I vs Class II Alleles ---
344+
345+ test_that(" calculatePeptideBindingLoad handles Class I B locus alleles" , {
346+ recipient <- hlaGeno(data.frame (B_1 = " B*07:02" , B_2 = " B*08:01" , stringsAsFactors = FALSE ))
347+ peptides <- c(" TPRVTGGGAM" ) # Known B*07:02 binder motif (Pro at P2)
348+
349+ result <- calculatePeptideBindingLoad(recipient , peptides , peptide_length = 10L , return = " detail" )
350+
351+ expect_true(nrow(result ) > = 1 )
352+ expect_true(all(grepl(" B\\ *" , result $ hla_allele )))
353+ })
354+
355+ # --- Empty and Edge Cases for Summary ---
356+
357+ test_that(" summary return handles empty peptides correctly" , {
358+ recipient <- hlaGeno(data.frame (A_1 = " A*02:01" , A_2 = " A*03:01" , stringsAsFactors = FALSE ))
359+
360+ result <- calculatePeptideBindingLoad(recipient , character (0 ), return = " summary" )
361+
362+ expect_s3_class(result , " data.frame" )
363+ expect_equal(nrow(result ), 2 )
364+ expect_true(all(result $ n_peptides == 0 ))
365+ expect_true(all(result $ n_strong == 0 ))
366+ expect_true(all(result $ n_weak == 0 ))
367+ expect_true(all(result $ risk_contribution == 0 ))
368+ })
369+
370+ test_that(" detail return handles empty peptides correctly" , {
371+ recipient <- hlaGeno(data.frame (A_1 = " A*02:01" , stringsAsFactors = FALSE ))
372+
373+ result <- calculatePeptideBindingLoad(recipient , character (0 ), return = " detail" )
374+
375+ expect_s3_class(result , " data.frame" )
376+ expect_equal(nrow(result ), 0 )
377+ expect_true(all(c(" peptide" , " hla_allele" , " predicted_ic50" , " binding_level" , " contribution" ) %in% names(result )))
378+ })
0 commit comments