From 043228b30ce13d679630c92e5946a51dabab834d Mon Sep 17 00:00:00 2001 From: Daniel Giurgiu Date: Fri, 20 Sep 2024 20:28:04 +0300 Subject: [PATCH] Add unit tests for GetAuthorsAndBooks and GetAuthorsAndBooksByID Handlers --- api/coverage.out | 546 +++++++++++++++++++++++------------------------ api/main.go | 160 ++++++-------- api/main_test.go | 236 ++++++++++++-------- 3 files changed, 489 insertions(+), 453 deletions(-) diff --git a/api/coverage.out b/api/coverage.out index 2f6d550..4536412 100644 --- a/api/coverage.out +++ b/api/coverage.out @@ -40,284 +40,282 @@ mymodule/main.go:182.33,184.3 1 1 mymodule/main.go:185.2,186.16 2 1 mymodule/main.go:190.62,193.2 2 1 mymodule/main.go:196.62,199.2 2 1 -mymodule/main.go:201.78,205.62 2 1 -mymodule/main.go:205.62,214.6 5 1 -mymodule/main.go:216.5,216.26 1 1 -mymodule/main.go:220.100,225.2 2 1 -mymodule/main.go:228.71,234.19 4 1 -mymodule/main.go:234.19,237.6 1 1 -mymodule/main.go:238.5,238.19 1 1 -mymodule/main.go:242.52,247.21 3 1 -mymodule/main.go:247.21,249.105 2 1 -mymodule/main.go:249.105,251.10 1 1 -mymodule/main.go:252.9,252.42 1 1 -mymodule/main.go:255.5,255.38 1 1 -mymodule/main.go:255.38,257.6 1 1 -mymodule/main.go:259.5,259.24 1 1 -mymodule/main.go:263.46,264.56 1 1 -mymodule/main.go:264.56,266.6 1 1 -mymodule/main.go:267.5,267.15 1 1 -mymodule/main.go:271.40,273.47 1 1 -mymodule/main.go:273.47,275.6 1 1 -mymodule/main.go:276.5,276.15 1 1 -mymodule/main.go:280.71,286.20 3 1 -mymodule/main.go:286.20,290.6 2 1 -mymodule/main.go:293.5,302.19 3 1 -mymodule/main.go:302.19,306.6 2 1 -mymodule/main.go:307.5,311.19 3 1 -mymodule/main.go:311.19,315.6 2 1 -mymodule/main.go:318.5,318.47 1 1 -mymodule/main.go:322.58,327.21 3 1 -mymodule/main.go:327.21,329.187 2 1 -mymodule/main.go:329.187,331.10 1 1 -mymodule/main.go:332.9,332.36 1 1 -mymodule/main.go:335.5,335.38 1 1 -mymodule/main.go:335.38,337.6 1 1 -mymodule/main.go:339.5,339.22 1 1 -mymodule/main.go:343.69,347.20 3 1 -mymodule/main.go:347.20,350.6 2 1 -mymodule/main.go:352.5,369.19 3 1 -mymodule/main.go:369.19,372.6 2 1 -mymodule/main.go:373.5,376.19 3 1 -mymodule/main.go:376.19,379.6 2 1 -mymodule/main.go:381.5,381.45 1 1 -mymodule/main.go:386.68,395.19 4 1 -mymodule/main.go:395.19,398.6 2 1 -mymodule/main.go:399.5,402.19 3 1 -mymodule/main.go:402.19,405.6 2 1 -mymodule/main.go:407.5,407.47 1 1 -mymodule/main.go:412.69,431.19 4 1 -mymodule/main.go:431.19,434.6 2 1 -mymodule/main.go:436.5,437.19 2 1 +mymodule/main.go:202.91,205.2 2 1 +mymodule/main.go:207.78,211.62 2 1 +mymodule/main.go:211.62,220.6 5 1 +mymodule/main.go:222.5,222.26 1 1 +mymodule/main.go:226.100,231.2 2 1 +mymodule/main.go:234.71,240.19 4 1 +mymodule/main.go:240.19,243.6 1 1 +mymodule/main.go:244.5,244.19 1 1 +mymodule/main.go:248.52,253.21 3 1 +mymodule/main.go:253.21,255.105 2 1 +mymodule/main.go:255.105,257.10 1 1 +mymodule/main.go:258.9,258.42 1 1 +mymodule/main.go:261.5,261.38 1 1 +mymodule/main.go:261.38,263.6 1 1 +mymodule/main.go:265.5,265.24 1 1 +mymodule/main.go:269.46,270.56 1 1 +mymodule/main.go:270.56,272.6 1 1 +mymodule/main.go:273.5,273.15 1 1 +mymodule/main.go:277.40,279.47 1 1 +mymodule/main.go:279.47,281.6 1 1 +mymodule/main.go:282.5,282.15 1 1 +mymodule/main.go:286.71,292.20 3 1 +mymodule/main.go:292.20,296.6 2 1 +mymodule/main.go:299.5,308.19 3 1 +mymodule/main.go:308.19,312.6 2 1 +mymodule/main.go:313.5,317.19 3 1 +mymodule/main.go:317.19,321.6 2 1 +mymodule/main.go:324.5,324.47 1 1 +mymodule/main.go:328.58,333.21 3 1 +mymodule/main.go:333.21,335.187 2 1 +mymodule/main.go:335.187,337.10 1 1 +mymodule/main.go:338.9,338.36 1 1 +mymodule/main.go:341.5,341.38 1 1 +mymodule/main.go:341.38,343.6 1 1 +mymodule/main.go:345.5,345.22 1 1 +mymodule/main.go:349.69,353.20 3 1 +mymodule/main.go:353.20,356.6 2 1 +mymodule/main.go:358.5,375.19 3 1 +mymodule/main.go:375.19,378.6 2 1 +mymodule/main.go:379.5,382.19 3 1 +mymodule/main.go:382.19,385.6 2 1 +mymodule/main.go:387.5,387.45 1 1 +mymodule/main.go:392.68,401.19 4 1 +mymodule/main.go:401.19,404.6 2 1 +mymodule/main.go:405.5,408.19 3 1 +mymodule/main.go:408.19,411.6 2 1 +mymodule/main.go:413.5,413.47 1 1 +mymodule/main.go:418.69,437.19 4 1 mymodule/main.go:437.19,440.6 2 1 -mymodule/main.go:442.5,442.45 1 1 -mymodule/main.go:447.76,455.16 3 1 -mymodule/main.go:455.16,459.3 3 0 -mymodule/main.go:460.2,463.18 3 1 -mymodule/main.go:463.18,465.138 2 1 -mymodule/main.go:465.138,469.4 3 0 -mymodule/main.go:470.3,470.56 1 1 -mymodule/main.go:473.2,473.35 1 1 -mymodule/main.go:473.35,477.3 3 0 -mymodule/main.go:479.2,480.67 2 1 -mymodule/main.go:480.67,483.3 2 0 -mymodule/main.go:487.76,491.16 4 1 -mymodule/main.go:491.16,494.3 2 0 -mymodule/main.go:496.2,505.16 3 1 -mymodule/main.go:505.16,509.3 3 0 -mymodule/main.go:510.2,515.18 4 1 -mymodule/main.go:515.18,517.108 2 1 -mymodule/main.go:517.108,521.4 3 0 -mymodule/main.go:522.3,525.5 1 1 -mymodule/main.go:528.2,528.35 1 1 -mymodule/main.go:528.35,532.3 3 0 -mymodule/main.go:534.2,547.66 3 1 -mymodule/main.go:547.66,550.3 2 0 -mymodule/main.go:554.69,557.16 3 1 -mymodule/main.go:557.16,560.3 2 0 -mymodule/main.go:562.2,578.16 3 1 -mymodule/main.go:578.16,582.3 3 0 -mymodule/main.go:583.2,586.17 3 1 -mymodule/main.go:586.17,587.181 1 1 -mymodule/main.go:587.181,591.4 3 0 -mymodule/main.go:592.8,595.3 2 0 -mymodule/main.go:597.2,598.56 2 1 -mymodule/main.go:598.56,601.3 2 0 -mymodule/main.go:605.80,607.18 2 1 -mymodule/main.go:607.18,610.3 2 0 -mymodule/main.go:612.2,620.16 3 1 -mymodule/main.go:620.16,624.3 3 0 -mymodule/main.go:625.2,628.18 3 1 -mymodule/main.go:628.18,630.99 2 1 -mymodule/main.go:630.99,634.4 3 0 -mymodule/main.go:635.3,635.48 1 1 -mymodule/main.go:638.2,638.35 1 1 -mymodule/main.go:638.35,642.3 3 0 -mymodule/main.go:644.2,644.27 1 1 -mymodule/main.go:644.27,647.3 2 0 -mymodule/main.go:649.2,650.63 2 1 -mymodule/main.go:650.63,653.3 2 0 -mymodule/main.go:657.75,660.16 3 1 -mymodule/main.go:660.16,664.3 3 0 -mymodule/main.go:665.2,668.18 3 1 -mymodule/main.go:668.18,670.99 2 1 -mymodule/main.go:670.99,674.4 3 0 -mymodule/main.go:675.3,675.48 1 1 -mymodule/main.go:678.2,678.35 1 1 -mymodule/main.go:678.35,682.3 3 0 -mymodule/main.go:684.2,685.63 2 1 -mymodule/main.go:685.63,688.3 2 0 -mymodule/main.go:692.72,693.33 1 1 -mymodule/main.go:693.33,696.3 2 0 -mymodule/main.go:698.2,700.16 3 1 -mymodule/main.go:700.16,703.3 2 0 -mymodule/main.go:705.2,706.16 2 1 -mymodule/main.go:706.16,710.3 3 0 -mymodule/main.go:711.2,720.16 7 1 -mymodule/main.go:720.16,724.3 3 0 -mymodule/main.go:726.2,727.16 2 1 -mymodule/main.go:727.16,731.3 3 0 -mymodule/main.go:732.2,735.16 3 1 -mymodule/main.go:735.16,739.3 3 0 +mymodule/main.go:442.5,443.19 2 1 +mymodule/main.go:443.19,446.6 2 1 +mymodule/main.go:448.5,448.45 1 1 +mymodule/main.go:452.76,463.19 4 1 +mymodule/main.go:463.19,467.6 3 1 +mymodule/main.go:468.5,471.19 3 1 +mymodule/main.go:471.19,475.6 3 1 +mymodule/main.go:477.5,477.55 1 1 +mymodule/main.go:481.76,485.19 3 1 +mymodule/main.go:485.19,488.6 2 1 +mymodule/main.go:490.5,499.19 3 1 +mymodule/main.go:499.19,502.6 2 1 +mymodule/main.go:503.5,506.19 3 1 +mymodule/main.go:506.19,509.6 2 1 +mymodule/main.go:511.5,511.54 1 1 +mymodule/main.go:515.63,518.21 2 1 +mymodule/main.go:518.21,520.144 2 1 +mymodule/main.go:520.144,522.10 1 0 +mymodule/main.go:523.9,523.62 1 1 +mymodule/main.go:526.5,526.38 1 1 +mymodule/main.go:526.38,528.6 1 1 +mymodule/main.go:530.5,530.32 1 1 +mymodule/main.go:534.69,537.16 3 1 +mymodule/main.go:537.16,540.3 2 0 +mymodule/main.go:542.2,558.16 3 1 +mymodule/main.go:558.16,562.3 3 0 +mymodule/main.go:563.2,566.17 3 1 +mymodule/main.go:566.17,567.181 1 1 +mymodule/main.go:567.181,571.4 3 0 +mymodule/main.go:572.8,575.3 2 0 +mymodule/main.go:577.2,578.56 2 1 +mymodule/main.go:578.56,581.3 2 0 +mymodule/main.go:585.80,587.18 2 1 +mymodule/main.go:587.18,590.3 2 0 +mymodule/main.go:592.2,600.16 3 1 +mymodule/main.go:600.16,604.3 3 0 +mymodule/main.go:605.2,608.18 3 1 +mymodule/main.go:608.18,610.99 2 1 +mymodule/main.go:610.99,614.4 3 0 +mymodule/main.go:615.3,615.48 1 1 +mymodule/main.go:618.2,618.35 1 1 +mymodule/main.go:618.35,622.3 3 0 +mymodule/main.go:624.2,624.27 1 1 +mymodule/main.go:624.27,627.3 2 0 +mymodule/main.go:629.2,630.63 2 1 +mymodule/main.go:630.63,633.3 2 0 +mymodule/main.go:637.75,640.16 3 1 +mymodule/main.go:640.16,644.3 3 0 +mymodule/main.go:645.2,648.18 3 1 +mymodule/main.go:648.18,650.99 2 1 +mymodule/main.go:650.99,654.4 3 0 +mymodule/main.go:655.3,655.48 1 1 +mymodule/main.go:658.2,658.35 1 1 +mymodule/main.go:658.35,662.3 3 0 +mymodule/main.go:664.2,665.63 2 1 +mymodule/main.go:665.63,668.3 2 0 +mymodule/main.go:672.72,673.33 1 1 +mymodule/main.go:673.33,676.3 2 0 +mymodule/main.go:678.2,680.16 3 1 +mymodule/main.go:680.16,683.3 2 0 +mymodule/main.go:685.2,686.16 2 1 +mymodule/main.go:686.16,690.3 3 0 +mymodule/main.go:691.2,700.16 7 1 +mymodule/main.go:700.16,704.3 3 0 +mymodule/main.go:706.2,707.16 2 1 +mymodule/main.go:707.16,711.3 3 0 +mymodule/main.go:712.2,715.16 3 1 +mymodule/main.go:715.16,719.3 3 0 +mymodule/main.go:721.2,723.16 3 1 +mymodule/main.go:723.16,727.3 3 0 +mymodule/main.go:729.2,730.63 2 1 +mymodule/main.go:734.67,735.33 1 1 +mymodule/main.go:735.33,738.3 2 0 mymodule/main.go:741.2,743.16 3 1 -mymodule/main.go:743.16,747.3 3 0 -mymodule/main.go:749.2,750.63 2 1 -mymodule/main.go:754.67,755.33 1 1 -mymodule/main.go:755.33,758.3 2 0 -mymodule/main.go:761.2,763.16 3 1 -mymodule/main.go:763.16,766.3 2 0 -mymodule/main.go:767.2,770.53 2 1 -mymodule/main.go:770.53,773.3 2 0 -mymodule/main.go:776.2,780.16 3 1 -mymodule/main.go:780.16,784.3 3 0 -mymodule/main.go:787.2,788.16 2 1 -mymodule/main.go:788.16,792.3 3 0 -mymodule/main.go:794.2,799.60 4 1 -mymodule/main.go:799.60,802.3 2 0 -mymodule/main.go:806.70,807.33 1 1 -mymodule/main.go:807.33,810.3 2 0 -mymodule/main.go:812.2,814.16 3 1 -mymodule/main.go:814.16,817.3 2 0 +mymodule/main.go:743.16,746.3 2 0 +mymodule/main.go:747.2,750.53 2 1 +mymodule/main.go:750.53,753.3 2 0 +mymodule/main.go:756.2,760.16 3 1 +mymodule/main.go:760.16,764.3 3 0 +mymodule/main.go:767.2,768.16 2 1 +mymodule/main.go:768.16,772.3 3 0 +mymodule/main.go:774.2,779.60 4 1 +mymodule/main.go:779.60,782.3 2 0 +mymodule/main.go:786.70,787.33 1 1 +mymodule/main.go:787.33,790.3 2 0 +mymodule/main.go:792.2,794.16 3 1 +mymodule/main.go:794.16,797.3 2 0 +mymodule/main.go:799.2,800.16 2 1 +mymodule/main.go:800.16,804.3 3 0 +mymodule/main.go:805.2,813.16 7 1 +mymodule/main.go:813.16,817.3 3 0 mymodule/main.go:819.2,820.16 2 1 mymodule/main.go:820.16,824.3 3 0 -mymodule/main.go:825.2,833.16 7 1 -mymodule/main.go:833.16,837.3 3 0 -mymodule/main.go:839.2,840.16 2 1 -mymodule/main.go:840.16,844.3 3 0 -mymodule/main.go:845.2,848.16 3 1 -mymodule/main.go:848.16,852.3 3 0 +mymodule/main.go:825.2,828.16 3 1 +mymodule/main.go:828.16,832.3 3 0 +mymodule/main.go:834.2,836.16 3 1 +mymodule/main.go:836.16,840.3 3 0 +mymodule/main.go:842.2,843.63 2 1 +mymodule/main.go:847.65,848.33 1 1 +mymodule/main.go:848.33,851.3 2 0 mymodule/main.go:854.2,856.16 3 1 -mymodule/main.go:856.16,860.3 3 0 -mymodule/main.go:862.2,863.63 2 1 -mymodule/main.go:867.65,868.33 1 1 -mymodule/main.go:868.33,871.3 2 0 -mymodule/main.go:874.2,876.16 3 1 -mymodule/main.go:876.16,879.3 2 0 -mymodule/main.go:880.2,883.44 2 1 -mymodule/main.go:883.44,886.3 2 0 -mymodule/main.go:889.2,893.16 3 1 -mymodule/main.go:893.16,897.3 3 0 -mymodule/main.go:900.2,901.16 2 1 -mymodule/main.go:901.16,905.3 3 0 -mymodule/main.go:907.2,911.60 4 1 -mymodule/main.go:911.60,914.3 2 0 -mymodule/main.go:918.71,919.33 1 1 -mymodule/main.go:919.33,922.3 2 0 -mymodule/main.go:925.2,927.16 3 1 -mymodule/main.go:927.16,930.3 2 0 -mymodule/main.go:931.2,934.87 2 1 -mymodule/main.go:934.87,937.3 2 0 -mymodule/main.go:940.2,944.16 3 1 -mymodule/main.go:944.16,948.3 3 0 -mymodule/main.go:951.2,952.16 2 1 -mymodule/main.go:952.16,956.3 3 0 -mymodule/main.go:958.2,962.60 4 1 -mymodule/main.go:962.60,965.3 2 0 -mymodule/main.go:969.68,970.33 1 1 -mymodule/main.go:970.33,973.3 2 0 -mymodule/main.go:976.2,981.16 3 1 -mymodule/main.go:981.16,984.3 2 0 -mymodule/main.go:986.2,986.62 1 1 -mymodule/main.go:986.62,989.3 2 0 -mymodule/main.go:992.2,994.16 3 1 +mymodule/main.go:856.16,859.3 2 0 +mymodule/main.go:860.2,863.44 2 1 +mymodule/main.go:863.44,866.3 2 0 +mymodule/main.go:869.2,873.16 3 1 +mymodule/main.go:873.16,877.3 3 0 +mymodule/main.go:880.2,881.16 2 1 +mymodule/main.go:881.16,885.3 3 0 +mymodule/main.go:887.2,891.60 4 1 +mymodule/main.go:891.60,894.3 2 0 +mymodule/main.go:898.71,899.33 1 1 +mymodule/main.go:899.33,902.3 2 0 +mymodule/main.go:905.2,907.16 3 1 +mymodule/main.go:907.16,910.3 2 0 +mymodule/main.go:911.2,914.87 2 1 +mymodule/main.go:914.87,917.3 2 0 +mymodule/main.go:920.2,924.16 3 1 +mymodule/main.go:924.16,928.3 3 0 +mymodule/main.go:931.2,932.16 2 1 +mymodule/main.go:932.16,936.3 3 0 +mymodule/main.go:938.2,942.60 4 1 +mymodule/main.go:942.60,945.3 2 0 +mymodule/main.go:949.68,950.33 1 1 +mymodule/main.go:950.33,953.3 2 0 +mymodule/main.go:956.2,961.16 3 1 +mymodule/main.go:961.16,964.3 2 0 +mymodule/main.go:966.2,966.62 1 1 +mymodule/main.go:966.62,969.3 2 0 +mymodule/main.go:972.2,974.16 3 1 +mymodule/main.go:974.16,978.3 3 0 +mymodule/main.go:979.2,979.16 1 1 +mymodule/main.go:979.16,982.3 2 0 +mymodule/main.go:985.2,986.16 2 1 +mymodule/main.go:986.16,990.3 3 0 +mymodule/main.go:993.2,994.16 2 1 mymodule/main.go:994.16,998.3 3 0 -mymodule/main.go:999.2,999.16 1 1 -mymodule/main.go:999.16,1002.3 2 0 -mymodule/main.go:1005.2,1006.16 2 1 -mymodule/main.go:1006.16,1010.3 3 0 -mymodule/main.go:1013.2,1014.16 2 1 -mymodule/main.go:1014.16,1018.3 3 0 -mymodule/main.go:1020.2,1022.61 3 1 -mymodule/main.go:1026.76,1027.33 1 1 -mymodule/main.go:1027.33,1030.3 2 0 -mymodule/main.go:1033.2,1038.16 3 1 -mymodule/main.go:1038.16,1041.3 2 0 -mymodule/main.go:1044.2,1046.16 3 1 -mymodule/main.go:1046.16,1047.27 1 0 -mymodule/main.go:1047.27,1049.4 1 0 -mymodule/main.go:1049.9,1052.4 2 0 -mymodule/main.go:1053.3,1053.9 1 0 -mymodule/main.go:1056.2,1056.17 1 1 -mymodule/main.go:1056.17,1059.3 2 0 -mymodule/main.go:1062.2,1063.16 2 1 -mymodule/main.go:1063.16,1067.3 3 0 -mymodule/main.go:1070.2,1071.16 2 1 -mymodule/main.go:1071.16,1075.3 3 0 -mymodule/main.go:1077.2,1078.46 2 1 -mymodule/main.go:1082.70,1083.63 1 1 -mymodule/main.go:1083.63,1086.3 2 0 -mymodule/main.go:1088.2,1090.16 3 1 -mymodule/main.go:1090.16,1093.3 2 0 -mymodule/main.go:1095.2,1097.16 3 1 -mymodule/main.go:1097.16,1100.3 2 0 -mymodule/main.go:1101.2,1103.53 2 1 -mymodule/main.go:1103.53,1106.3 2 0 -mymodule/main.go:1108.2,1115.16 3 1 -mymodule/main.go:1115.16,1119.3 3 0 -mymodule/main.go:1121.2,1122.23 2 1 -mymodule/main.go:1122.23,1125.3 2 0 -mymodule/main.go:1127.2,1127.47 1 1 -mymodule/main.go:1131.68,1132.63 1 1 -mymodule/main.go:1132.63,1135.3 2 0 -mymodule/main.go:1138.2,1140.16 3 1 -mymodule/main.go:1140.16,1143.3 2 0 -mymodule/main.go:1146.2,1154.16 3 1 -mymodule/main.go:1154.16,1157.3 2 0 -mymodule/main.go:1158.2,1161.44 2 1 -mymodule/main.go:1161.44,1164.3 2 0 -mymodule/main.go:1167.2,1175.16 3 1 -mymodule/main.go:1175.16,1179.3 3 0 -mymodule/main.go:1182.2,1183.23 2 1 -mymodule/main.go:1183.23,1186.3 2 0 -mymodule/main.go:1188.2,1188.45 1 1 -mymodule/main.go:1192.74,1193.63 1 0 -mymodule/main.go:1193.63,1196.3 2 0 -mymodule/main.go:1199.2,1201.16 3 0 -mymodule/main.go:1201.16,1204.3 2 0 -mymodule/main.go:1207.2,1209.16 3 0 -mymodule/main.go:1209.16,1212.3 2 0 -mymodule/main.go:1213.2,1220.87 4 0 -mymodule/main.go:1220.87,1223.3 2 0 -mymodule/main.go:1226.2,1234.16 3 0 -mymodule/main.go:1234.16,1238.3 3 0 -mymodule/main.go:1241.2,1242.23 2 0 -mymodule/main.go:1242.23,1245.3 2 0 -mymodule/main.go:1247.2,1247.51 1 0 -mymodule/main.go:1251.70,1252.35 1 0 -mymodule/main.go:1252.35,1255.3 2 0 -mymodule/main.go:1258.2,1260.16 3 0 -mymodule/main.go:1260.16,1263.3 2 0 -mymodule/main.go:1266.2,1275.16 4 0 +mymodule/main.go:1000.2,1002.61 3 1 +mymodule/main.go:1006.76,1007.33 1 1 +mymodule/main.go:1007.33,1010.3 2 0 +mymodule/main.go:1013.2,1018.16 3 1 +mymodule/main.go:1018.16,1021.3 2 0 +mymodule/main.go:1024.2,1026.16 3 1 +mymodule/main.go:1026.16,1027.27 1 0 +mymodule/main.go:1027.27,1029.4 1 0 +mymodule/main.go:1029.9,1032.4 2 0 +mymodule/main.go:1033.3,1033.9 1 0 +mymodule/main.go:1036.2,1036.17 1 1 +mymodule/main.go:1036.17,1039.3 2 0 +mymodule/main.go:1042.2,1043.16 2 1 +mymodule/main.go:1043.16,1047.3 3 0 +mymodule/main.go:1050.2,1051.16 2 1 +mymodule/main.go:1051.16,1055.3 3 0 +mymodule/main.go:1057.2,1058.46 2 1 +mymodule/main.go:1062.70,1063.63 1 1 +mymodule/main.go:1063.63,1066.3 2 0 +mymodule/main.go:1068.2,1070.16 3 1 +mymodule/main.go:1070.16,1073.3 2 0 +mymodule/main.go:1075.2,1077.16 3 1 +mymodule/main.go:1077.16,1080.3 2 0 +mymodule/main.go:1081.2,1083.53 2 1 +mymodule/main.go:1083.53,1086.3 2 0 +mymodule/main.go:1088.2,1095.16 3 1 +mymodule/main.go:1095.16,1099.3 3 0 +mymodule/main.go:1101.2,1102.23 2 1 +mymodule/main.go:1102.23,1105.3 2 0 +mymodule/main.go:1107.2,1107.47 1 1 +mymodule/main.go:1111.68,1112.63 1 1 +mymodule/main.go:1112.63,1115.3 2 0 +mymodule/main.go:1118.2,1120.16 3 1 +mymodule/main.go:1120.16,1123.3 2 0 +mymodule/main.go:1126.2,1134.16 3 1 +mymodule/main.go:1134.16,1137.3 2 0 +mymodule/main.go:1138.2,1141.44 2 1 +mymodule/main.go:1141.44,1144.3 2 0 +mymodule/main.go:1147.2,1155.16 3 1 +mymodule/main.go:1155.16,1159.3 3 0 +mymodule/main.go:1162.2,1163.23 2 1 +mymodule/main.go:1163.23,1166.3 2 0 +mymodule/main.go:1168.2,1168.45 1 1 +mymodule/main.go:1172.74,1173.63 1 0 +mymodule/main.go:1173.63,1176.3 2 0 +mymodule/main.go:1179.2,1181.16 3 0 +mymodule/main.go:1181.16,1184.3 2 0 +mymodule/main.go:1187.2,1189.16 3 0 +mymodule/main.go:1189.16,1192.3 2 0 +mymodule/main.go:1193.2,1200.87 4 0 +mymodule/main.go:1200.87,1203.3 2 0 +mymodule/main.go:1206.2,1214.16 3 0 +mymodule/main.go:1214.16,1218.3 3 0 +mymodule/main.go:1221.2,1222.23 2 0 +mymodule/main.go:1222.23,1225.3 2 0 +mymodule/main.go:1227.2,1227.51 1 0 +mymodule/main.go:1231.70,1232.35 1 0 +mymodule/main.go:1232.35,1235.3 2 0 +mymodule/main.go:1238.2,1240.16 3 0 +mymodule/main.go:1240.16,1243.3 2 0 +mymodule/main.go:1246.2,1255.16 4 0 +mymodule/main.go:1255.16,1259.3 3 0 +mymodule/main.go:1262.2,1262.18 1 0 +mymodule/main.go:1262.18,1265.3 2 0 +mymodule/main.go:1268.2,1275.16 3 0 mymodule/main.go:1275.16,1279.3 3 0 -mymodule/main.go:1282.2,1282.18 1 0 -mymodule/main.go:1282.18,1285.3 2 0 -mymodule/main.go:1288.2,1295.16 3 0 -mymodule/main.go:1295.16,1299.3 3 0 -mymodule/main.go:1302.2,1303.23 2 0 -mymodule/main.go:1303.23,1306.3 2 0 -mymodule/main.go:1308.2,1308.47 1 0 -mymodule/main.go:1312.68,1313.35 1 0 -mymodule/main.go:1313.35,1316.3 2 0 -mymodule/main.go:1319.2,1321.16 3 0 -mymodule/main.go:1321.16,1324.3 2 0 -mymodule/main.go:1327.2,1336.16 4 0 -mymodule/main.go:1336.16,1340.3 3 0 -mymodule/main.go:1343.2,1352.16 4 0 -mymodule/main.go:1352.16,1356.3 3 0 -mymodule/main.go:1359.2,1366.16 3 0 -mymodule/main.go:1366.16,1370.3 3 0 -mymodule/main.go:1373.2,1374.23 2 0 -mymodule/main.go:1374.23,1377.3 2 0 -mymodule/main.go:1380.2,1380.24 1 0 -mymodule/main.go:1380.24,1388.17 3 0 -mymodule/main.go:1388.17,1392.4 3 0 -mymodule/main.go:1395.2,1395.45 1 0 -mymodule/main.go:1399.74,1400.35 1 0 -mymodule/main.go:1400.35,1403.3 2 0 -mymodule/main.go:1406.2,1408.16 3 0 -mymodule/main.go:1408.16,1411.3 2 0 -mymodule/main.go:1414.2,1421.16 3 0 -mymodule/main.go:1421.16,1425.3 3 0 -mymodule/main.go:1428.2,1429.23 2 0 -mymodule/main.go:1429.23,1432.3 2 0 -mymodule/main.go:1434.2,1434.51 1 0 +mymodule/main.go:1282.2,1283.23 2 0 +mymodule/main.go:1283.23,1286.3 2 0 +mymodule/main.go:1288.2,1288.47 1 0 +mymodule/main.go:1292.68,1293.35 1 0 +mymodule/main.go:1293.35,1296.3 2 0 +mymodule/main.go:1299.2,1301.16 3 0 +mymodule/main.go:1301.16,1304.3 2 0 +mymodule/main.go:1307.2,1316.16 4 0 +mymodule/main.go:1316.16,1320.3 3 0 +mymodule/main.go:1323.2,1332.16 4 0 +mymodule/main.go:1332.16,1336.3 3 0 +mymodule/main.go:1339.2,1346.16 3 0 +mymodule/main.go:1346.16,1350.3 3 0 +mymodule/main.go:1353.2,1354.23 2 0 +mymodule/main.go:1354.23,1357.3 2 0 +mymodule/main.go:1360.2,1360.24 1 0 +mymodule/main.go:1360.24,1368.17 3 0 +mymodule/main.go:1368.17,1372.4 3 0 +mymodule/main.go:1375.2,1375.45 1 0 +mymodule/main.go:1379.74,1380.35 1 0 +mymodule/main.go:1380.35,1383.3 2 0 +mymodule/main.go:1386.2,1388.16 3 0 +mymodule/main.go:1388.16,1391.3 2 0 +mymodule/main.go:1394.2,1401.16 3 0 +mymodule/main.go:1401.16,1405.3 3 0 +mymodule/main.go:1408.2,1409.23 2 0 +mymodule/main.go:1409.23,1412.3 2 0 +mymodule/main.go:1414.2,1414.51 1 0 diff --git a/api/main.go b/api/main.go index f825825..d5cb8df 100644 --- a/api/main.go +++ b/api/main.go @@ -198,6 +198,12 @@ func (app *App) Info(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Info page") } +// HandleError is a method of App that handles errors by logging them and sending an appropriate HTTP response +func (app *App) HandleError(w http.ResponseWriter, message string, err error, status int) { + app.Logger.Printf("%s: %v", message, err) + http.Error(w, message, status) +} + func RespondWithJSON(w http.ResponseWriter, status int, payload interface{}) { // Set Content-Type header to application/json w.Header().Set("Content-Type", "application/json") @@ -442,112 +448,86 @@ func (app *App) GetAllBooks(w http.ResponseWriter, r *http.Request) { RespondWithJSON(w, http.StatusOK, books) } - // GetAuthorsAndBooks retrieves information about authors and their books func (app *App) GetAuthorsAndBooks(w http.ResponseWriter, r *http.Request) { - query := ` - SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo - FROM authors_books ab - JOIN authors a ON ab.author_id = a.id - JOIN books b ON ab.book_id = b.id - ` - rows, err := app.DB.Query(query) - if err != nil { - app.Logger.Printf("Query error: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - defer rows.Close() + app.Logger.Println("GetAuthorsAndBooks handler called") - var authorsAndBooks []AuthorBook - for rows.Next() { - var authorBook AuthorBook - if err := rows.Scan(&authorBook.AuthorFirstname, &authorBook.AuthorLastname, &authorBook.BookTitle, &authorBook.BookPhoto); err != nil { - app.Logger.Printf("Scan error: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - authorsAndBooks = append(authorsAndBooks, authorBook) - } + query := ` + SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo + FROM authors_books ab + JOIN authors a ON ab.author_id = a.id + JOIN books b ON ab.book_id = b.id + ` - if err := rows.Err(); err != nil { - app.Logger.Printf("Row iteration error: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } + rows, err := app.DB.Query(query) + if err != nil { + app.Logger.Printf("Query error: %v", err) + HandleError(w, app.Logger, "Error executing query", err, http.StatusInternalServerError) + return + } + defer rows.Close() - w.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(w).Encode(authorsAndBooks); err != nil { - app.Logger.Printf("JSON encoding error: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - } + authorsAndBooks, err := ScanAuthorAndBooks(rows) + if err != nil { + app.Logger.Printf("Scan error: %v", err) + HandleError(w, app.Logger, "Error scanning authors and books", err, http.StatusInternalServerError) + return + } + + RespondWithJSON(w, http.StatusOK, authorsAndBooks) } // GetAuthorBooksByID retrieves information about an author and their books by the author's ID func (app *App) GetAuthorBooksByID(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - authorID := vars["id"] - id, err := strconv.Atoi(authorID) - if err != nil { - http.Error(w, "Invalid author ID", http.StatusBadRequest) - return - } + app.Logger.Println("GetAuthorBooksByID handler called") - query := ` - SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, a.Photo AS author_photo, b.title AS book_title, b.photo AS book_photo - FROM authors_books ab - JOIN authors a ON ab.author_id = a.id - JOIN books b ON ab.book_id = b.id - WHERE a.id = ? - ` + authorID, err := GetIDFromRequest(r, "id") + if err != nil { + app.HandleError(w, "Invalid author ID", err, http.StatusBadRequest) + return + } - rows, err := app.DB.Query(query, id) - if err != nil { - app.Logger.Printf("Query error: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - defer rows.Close() + query := ` + SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo + FROM authors_books ab + JOIN authors a ON ab.author_id = a.id + JOIN books b ON ab.book_id = b.id + WHERE a.id = ? + ` - var authorFirstname, authorLastname, authorPhoto string - var books []AuthorBook + rows, err := app.DB.Query(query, authorID) + if err != nil { + app.HandleError(w, "Error executing query", err, http.StatusInternalServerError) + return + } + defer rows.Close() - for rows.Next() { - var bookTitle, bookPhoto string - if err := rows.Scan(&authorFirstname, &authorLastname, &authorPhoto, &bookTitle, &bookPhoto); err != nil { - app.Logger.Printf("Scan error: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - books = append(books, AuthorBook{ - BookTitle: bookTitle, - BookPhoto: bookPhoto, - }) - } + authorAndBooks, err := ScanAuthorAndBooks(rows) + if err != nil { + app.HandleError(w, "Error scanning results", err, http.StatusInternalServerError) + return + } - if err := rows.Err(); err != nil { - app.Logger.Printf("Row iteration error: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } + RespondWithJSON(w, http.StatusOK, authorAndBooks) +} - authorAndBooks := struct { - AuthorFirstname string `json:"author_firstname"` - AuthorLastname string `json:"author_lastname"` - AuthorPhoto string `json:"author_photo"` - Books []AuthorBook `json:"books"` - }{ - AuthorFirstname: authorFirstname, - AuthorLastname: authorLastname, - AuthorPhoto: authorPhoto, - Books: books, - } +// ScanAuthorAndBooks processes rows from the SQL query and returns a list of author and book information +func ScanAuthorAndBooks(rows *sql.Rows) ([]AuthorBook, error) { + var authorsAndBooks []AuthorBook - w.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(w).Encode(authorAndBooks); err != nil { - app.Logger.Printf("JSON encoding error: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - } + for rows.Next() { + var authorBook AuthorBook + if err := rows.Scan(&authorBook.AuthorFirstname, &authorBook.AuthorLastname, &authorBook.BookTitle, &authorBook.BookPhoto); err != nil { + return nil, err + } + authorsAndBooks = append(authorsAndBooks, authorBook) + } + + if err := rows.Err(); err != nil { + return nil, err + } + + return authorsAndBooks, nil } // GetBookByID retrieves information about a specific book based on its ID diff --git a/api/main_test.go b/api/main_test.go index b2a6ed3..4071af1 100644 --- a/api/main_test.go +++ b/api/main_test.go @@ -60,7 +60,6 @@ func TestInitDB(t *testing.T) { dsn := "user:password@tcp(localhost:3306)/testdb" - originalSQLOpen := sqlOpen sqlOpen = func(driverName, dataSourceName string) (*sql.DB, error) { if dataSourceName == dsn { @@ -228,7 +227,6 @@ func TestRespondWithJSON(t *testing.T) { RespondWithJSON(rr, http.StatusOK, payload) assert.Equal(t, "application/json", rr.Header().Get("Content-Type"), "Content-Type should be application/json") - assert.Equal(t, http.StatusOK, rr.Code, "Expected status code 200") expectedBody, _ := json.Marshal(payload) @@ -625,8 +623,7 @@ func TestScanBooks_ErrorAfterIteration(t *testing.T) { } } - -/// TestGetAuthors tests the GetAuthors handler with Dependency Injection +// TestGetAuthors tests the GetAuthors handler with Dependency Injection func TestGetAuthors(t *testing.T) { app, mock := createTestApp(t) defer app.DB.Close() @@ -714,7 +711,6 @@ func TestGetAuthors_ErrorScanningRows(t *testing.T) { } } - // TestGetAllBooks tests the GetAllBooks handler func TestGetAllBooks_Success(t *testing.T) { app, mock := createTestApp(t) @@ -767,7 +763,6 @@ func TestGetAllBooks_ErrorQuery(t *testing.T) { assert.Contains(t, rr.Body.String(), "Error executing query", "Expected 'Error executing query' in response") } - func TestGetAllBooks_ErrorScan(t *testing.T) { app, mock := createTestApp(t) defer app.DB.Close() @@ -792,109 +787,172 @@ func TestGetAllBooks_ErrorScan(t *testing.T) { assert.Contains(t, rr.Body.String(), "Error scanning books") } +// GetAuthorsAndBooks handler tests +func TestGetAuthorsAndBooks_Success(t *testing.T) { + app, mock := createTestApp(t) + defer app.DB.Close() -// TestGetAuthorsAndBooks tests the GetAuthorsAndBooks handler -func TestGetAuthorsAndBooks(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() + req, err := http.NewRequest("GET", "/authorsbooks", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") - // Setting up SQL mock expectations - rows := sqlmock.NewRows([]string{"author_firstname", "author_lastname", "book_title", "book_photo"}). - AddRow("John", "Doe", "Book Title 1", "book1.jpg"). - AddRow("Jane", "Smith", "Book Title 2", "book2.jpg") + rr := httptest.NewRecorder() - mock.ExpectQuery("SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo"). - WillReturnRows(rows) + rows := sqlmock.NewRows([]string{"author_firstname", "author_lastname", "book_title", "book_photo"}). + AddRow("John", "Doe", "Book 1", "book1.jpg"). + AddRow("Jane", "Smith", "Book 2", "book2.jpg") - // Creating a new HTTP request - req, err := http.NewRequest("GET", "/authorsbooks", nil) - if err != nil { - t.Fatalf("Could not create request: %v", err) - } + mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id`). + WillReturnRows(rows) - // Capturing the response - rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.GetAuthorsAndBooks) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetAuthorsAndBooks) + handler.ServeHTTP(rr, req) - // Ensuring the response status is 200 OK - assert.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, http.StatusOK, rr.Code, "Expected status code 200") - // Checking the JSON response - var authorsAndBooks []AuthorBook - err = json.NewDecoder(rr.Body).Decode(&authorsAndBooks) - if err != nil { - t.Fatalf("Could not decode response: %v", err) - } + expected := []map[string]interface{}{ + {"author_firstname": "John", "author_lastname": "Doe", "book_title": "Book 1", "book_photo": "book1.jpg"}, + {"author_firstname": "Jane", "author_lastname": "Smith", "book_title": "Book 2", "book_photo": "book2.jpg"}, + } + var actual []map[string]interface{} + err = json.Unmarshal(rr.Body.Bytes(), &actual) + assert.NoError(t, err, "Expected no error while unmarshaling JSON response") + assert.Equal(t, expected, actual, "Expected JSON response") +} - // Verifying the response data - assert.Equal(t, 2, len(authorsAndBooks)) - assert.Equal(t, "John", authorsAndBooks[0].AuthorFirstname) - assert.Equal(t, "Doe", authorsAndBooks[0].AuthorLastname) - assert.Equal(t, "Book Title 1", authorsAndBooks[0].BookTitle) - assert.Equal(t, "book1.jpg", authorsAndBooks[0].BookPhoto) +func TestGetAuthorsAndBooks_ErrorExecutingQuery(t *testing.T) { + app, mock := createTestApp(t) + defer app.DB.Close() - // Ensuring all mock expectations were met - if err := mock.ExpectationsWereMet(); err != nil { - t.Errorf("Not all expectations were met: %v", err) - } + req, err := http.NewRequest("GET", "/authorsbooks", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") + + rr := httptest.NewRecorder() + + mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id`). + WillReturnError(fmt.Errorf("query execution error")) + + handler := http.HandlerFunc(app.GetAuthorsAndBooks) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") + assert.Contains(t, rr.Body.String(), "Error executing query", "Expected error message for query execution error") } -// TestGetAuthorBooksByID tests the GetAuthorBooksByID handler -func TestGetAuthorBooksByID(t *testing.T) { - app, mock := createTestApp(t) - defer app.DB.Close() +func TestGetAuthorsAndBooks_ErrorScanningRows(t *testing.T) { + app, mock := createTestApp(t) + defer app.DB.Close() - authorID := "1" + req, err := http.NewRequest("GET", "/authorsbooks", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") - // Setting up SQL mock expectations - rows := sqlmock.NewRows([]string{"author_firstname", "author_lastname", "author_photo", "book_title", "book_photo"}). - AddRow("John", "Doe", "john.jpg", "Book Title 1", "book1.jpg"). - AddRow("John", "Doe", "john.jpg", "Book Title 2", "book2.jpg") + rr := httptest.NewRecorder() - mock.ExpectQuery("SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, a.Photo AS author_photo, b.title AS book_title, b.photo AS book_photo"). - WithArgs(1). - WillReturnRows(rows) + rows := sqlmock.NewRows([]string{"author_firstname", "author_lastname", "book_title", "book_photo"}). + AddRow("invalid_data", "Doe", "Book 1", "book1.jpg"). + RowError(0, fmt.Errorf("scan error")) - // Creating a new HTTP request - req, err := http.NewRequest("GET", "/authors/1", nil) - if err != nil { - t.Fatalf("Could not create request: %v", err) - } + mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id`). + WillReturnRows(rows) - // Capturing the response - rr := httptest.NewRecorder() - handler := http.HandlerFunc(app.GetAuthorBooksByID) - // Using mux.Vars to mock the ID parameter - req = mux.SetURLVars(req, map[string]string{"id": authorID}) - handler.ServeHTTP(rr, req) + handler := http.HandlerFunc(app.GetAuthorsAndBooks) + handler.ServeHTTP(rr, req) - // Ensuring the response status is 200 OK - assert.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") + assert.Contains(t, rr.Body.String(), "Error scanning authors and books", "Expected error message for scan error") +} - // Checking the JSON response - var authorAndBooks struct { - AuthorFirstname string `json:"author_firstname"` - AuthorLastname string `json:"author_lastname"` - AuthorPhoto string `json:"author_photo"` - Books []AuthorBook `json:"books"` - } - err = json.NewDecoder(rr.Body).Decode(&authorAndBooks) - if err != nil { - t.Fatalf("Could not decode response: %v", err) - } +// GetAuthorsAndBooksByID tests +func TestGetAuthorBooksByID_Success(t *testing.T) { + app, mock := createTestApp(t) + defer app.DB.Close() - // Verifying the response data - assert.Equal(t, "John", authorAndBooks.AuthorFirstname) - assert.Equal(t, "Doe", authorAndBooks.AuthorLastname) - assert.Equal(t, "john.jpg", authorAndBooks.AuthorPhoto) - assert.Equal(t, 2, len(authorAndBooks.Books)) - assert.Equal(t, "Book Title 1", authorAndBooks.Books[0].BookTitle) + req, err := http.NewRequest("GET", "/authors/1", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") + req = mux.SetURLVars(req, map[string]string{"id": "1"}) - // Ensuring all mock expectations were met - if err := mock.ExpectationsWereMet(); err != nil { - t.Errorf("Not all expectations were met: %v", err) - } + rr := httptest.NewRecorder() + + rows := sqlmock.NewRows([]string{"author_firstname", "author_lastname", "book_title", "book_photo"}). + AddRow("John", "Doe", "Book 1", "book1.jpg"). + AddRow("John", "Doe", "Book 2", "book2.jpg") + + mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id WHERE a.id = ?`). + WithArgs(1).WillReturnRows(rows) + + handler := http.HandlerFunc(app.GetAuthorBooksByID) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusOK, rr.Code, "Expected status code 200") + + expected := []map[string]interface{}{ + {"author_firstname": "John", "author_lastname": "Doe", "book_title": "Book 1", "book_photo": "book1.jpg"}, + {"author_firstname": "John", "author_lastname": "Doe", "book_title": "Book 2", "book_photo": "book2.jpg"}, + } + var actual []map[string]interface{} + err = json.Unmarshal(rr.Body.Bytes(), &actual) + assert.NoError(t, err, "Expected no error while unmarshaling JSON response") + assert.Equal(t, expected, actual, "Expected JSON response") +} + +func TestGetAuthorBooksByID_ErrorInvalidID(t *testing.T) { + app, _ := createTestApp(t) + defer app.DB.Close() + + req, err := http.NewRequest("GET", "/authors/abc", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") + req = mux.SetURLVars(req, map[string]string{"id": "abc"}) + + rr := httptest.NewRecorder() + + handler := http.HandlerFunc(app.GetAuthorBooksByID) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusBadRequest, rr.Code, "Expected status code 400") + assert.Contains(t, rr.Body.String(), "Invalid author ID", "Expected 'Invalid author ID' in response") +} + +func TestGetAuthorBooksByID_ErrorExecutingQuery(t *testing.T) { + app, mock := createTestApp(t) + defer app.DB.Close() + + req, err := http.NewRequest("GET", "/authors/1", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") + req = mux.SetURLVars(req, map[string]string{"id": "1"}) + + rr := httptest.NewRecorder() + + mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id WHERE a.id = ?`). + WithArgs(1).WillReturnError(fmt.Errorf("query execution error")) + + handler := http.HandlerFunc(app.GetAuthorBooksByID) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") + assert.Contains(t, rr.Body.String(), "Error executing query", "Expected error message for query execution error") +} + +func TestGetAuthorBooksByID_ErrorScanningRows(t *testing.T) { + app, mock := createTestApp(t) + defer app.DB.Close() + + req, err := http.NewRequest("GET", "/authors/1", nil) + assert.NoError(t, err, "Error should be nil when creating a new request") + req = mux.SetURLVars(req, map[string]string{"id": "1"}) + + rr := httptest.NewRecorder() + + rows := sqlmock.NewRows([]string{"author_firstname", "author_lastname", "book_title", "book_photo"}). + AddRow("invalid_data", "Doe", "Book 1", "book1.jpg"). + RowError(0, fmt.Errorf("scan error")) + + mock.ExpectQuery(`SELECT a.Firstname AS author_firstname, a.Lastname AS author_lastname, b.title AS book_title, b.photo AS book_photo FROM authors_books ab JOIN authors a ON ab.author_id = a.id JOIN books b ON ab.book_id = b.id WHERE a.id = ?`). + WithArgs(1).WillReturnRows(rows) + + handler := http.HandlerFunc(app.GetAuthorBooksByID) + handler.ServeHTTP(rr, req) + + assert.Equal(t, http.StatusInternalServerError, rr.Code, "Expected status code 500") + assert.Contains(t, rr.Body.String(), "Error scanning results", "Expected error message for scan error") } // TestGetBookByID tests the GetBookByID handler