From ff55338459cff0b83b1a4de321b000de02b2920b Mon Sep 17 00:00:00 2001 From: Ryan Murphy Date: Fri, 15 Sep 2023 10:27:38 -0400 Subject: [PATCH] 27: Add pagination support --- elixir/github_repo_cloner | Bin 1340122 -> 1341150 bytes elixir/lib/cli.ex | 8 ++++--- elixir/lib/cloner.ex | 19 +++++++++------- elixir/lib/page_iterator.ex | 12 ++++++++++ elixir/test/cloner_test.exs | 34 +++++++++++++++-------------- elixir/test/page_iterator_test.exs | 23 +++++++++++++++++++ 6 files changed, 69 insertions(+), 27 deletions(-) create mode 100644 elixir/lib/page_iterator.ex create mode 100644 elixir/test/page_iterator_test.exs diff --git a/elixir/github_repo_cloner b/elixir/github_repo_cloner index 38245ab00e50589493c2b8e537fb4a35e084db8f..d1a2154f9114e3e91b2f9a824781891b957d5fa7 100755 GIT binary patch delta 16039 zcmZuX2V9Na|95p~x4WM9UfPMQw4*ddqEb|nw6k6_<+YQHaI!^q%FfIvD=W!NBC<8C zH^+a zNYuK`Mhj=X5&5^~#vwLP zAHB+&J3}2Ue&nCSug-u*%)D?D6JzZxw6;TgYHo$%%~mfjt0Ks!Ryz?|m~@OovX?Dp zF*hKtWo9bO4QBMqvI|zs8yL3ImRVzjVDUlBzYzJdryx#M6-(uGl^N0p!SXto z)8%Nzm$0rIqt$%})@>}`9!FN5fQIdMa%Evumq9F{7TV?)$+~ZaRwfHsKhU-^!xb!| z(3etHUu2y!+e@s#nP}f^H}-xVw6cm|A4aY$dpd!A3kzDB#AXZ8+hbGNqcE;o4tqQD zOxcm$Y+@kW1MC=VmK7!J%N7VD{60G!i(K`PeHE|P*0G~d2$X$kWb-iW*Gu*|%q{K> zd%GUO<*RTmV58j-ay0O&Ny5=)sX@iVsGTk+0#z7{!91WU=h$S8Nufqkc?Bh>q7i@*QX{v`c=H<-q+o;L{`f<$3A`DmU#Gd{n?}J(~d80*d~gThWnjwt(I2j zCi?fy9yR>J*1;9?RpM$ZwW59d+~Js-_bTTLBpZBPjyQ{#FWtCl_HEbKPrl#!erwC= zkJrZhV?xcCas6uRi;8bgsVuK2rToIDUroKI`S<;Kcb{v7LBl20%;ez@3LZt*F9`Ix z_wDWaK!=37T?5A+U$1`kz4;)!!!h%;dt85=GHOr!h(&3SN9_vr&G}h1`st`?H}_dJ zecF{4>u`U~7q637lWgm3U#AxDy`(ciW6I;o4T1>?`)&zC99kMD?r;R*)H|P9ddR%`LZ&PqBG}ckOam*4RFsJ_m*^rq|1>H6d1tY_gW9961zL%bdP%(}^=W6d|$-(7dp ze?YW#ZHdXA!gO^Rd*hGnkL80KHmKJ)ejlec}27kPFE zCRg)kwS~OAQmg6g*759{sUmFX;_|2GvTWAmNlO+o{@!=)vyrQj$q>DlqG{5hGx_}o zFR0#SniZM%!Fq+`l$L-d#gL~u-z~41zfrfHvSv|?{?0^BPy4xdk$Huy8j{jnC+U5 z-XSYpdK}%J&~f37s&Iv!;J`@r5!#|kM#qHLtnoD}O`hNE1_bS&DzuYtAJ6&O>N|@X zz53R;1gmW~R&tzPtTx=c?#Qjw83%b2lct^ancx(gd5>e5a=I?Sb5fSig;O#{-Rq(> z7m2(uG0wqNaUft=S>*g!>np26hD`O%ne4SV(roHMFV=0rog!+P*l+VD>5g7tUic-& ziv_oeYSnkeZcAWWOr6(wIIJ*Xet@Lqj^gv1?~;+AzNFOQn9-Nex6iLEsg4{O7iTsb z92v&SY3`A8JvVUvB+E3WX_nR(*VZn>O$*i^Gtkqzs-4s|IDXsD*zvq42~|2;dsJWU zXdcm*GjwO~ox)kg<4;Uq@%ITw^LbC$jqFvP7x#bR21oO1-l^@;Joog7jnT*bAN02v zu-|_9B5p38-DVSR({{EbbNs&5wwval)LhFQzOPUtd*WvEtWRJ;~P8 zGY0Ete4Mplb8_z1h`F7YBWl;5da`!Oxx4dcHOB^U+G|77AM{lD6(UUUFX^gv&iwG% z`-e~6^mAuFJvzO0Do?8D=XK0^r1)vSHD1%R?7OC=H5cxFdd9NfGP}ls!!;`Udz)B~ zvsj(;uHB>j$G9VVvzWsI*c^(%VDK4qdsNlr9O205+I!-8=1vDTgAv7HFpR*Rmr`|b z-h@&s`>e|uh^x4RAtxSJ>;f~+0SeV~Unfo;YT0FvTsbYc?qB!d+`+4@gE+4+?vf!K zKNVEtTVgI4bJZA(CF&XP;Cy2XXd!oG z>$Ze}C;ND&pxTh)0*4-ypv*3r6N@*lO6BNbX%0@}6ytsrJ(IIS6OnFP$yti!&s@j( zh4;MY|t!jvDK;=JpL_FaC*(J)7=28qgiDO$xFsXW3M zo;_45vBrFBl~1@&cRH$^z-|A8kBShF2bW@1#$oxa6ICLxEQ?ZArefUA43z_T0yr>T zDyF-0+9q)KByeVROYm)oW?Q_{Gy_ZB?#|S`Hble<#6}m zw?Dbu2rNOE8kbt)gle+-Hj@9yN@Eo&O#Os0S)~NAb;(6z!9z6ieLOg|{E;_{^7A zgKcpvfY*XG+a1aq7KB**DCB*@Z(E9ZFR=jk_wm+YSo#5;GuHdSQQmVygt4lYw+>CD zW!BGl1$f`3MxGny#%`e$1;yr;}e^u$@pWjuWoDd4X_@qX8fa= z-fj!N7!Ryv1NmYs^A#_?zY)ro*@yJmR-0aJ*-wyLIgdt1kz`{a4Mh+&-$% zLY6b^OXwM@bnLqe`TV~%FI-EjRh!dtc4kY{Mv(sMVv|>YhVT2^@r#e7bY!VAp6M^1 zWt$M$ci9B4T|f0T!pbSUln2Ir9FvBwHul+bEows_)$8js#Dn^1?-*qjQ<-jcdgSfH zOT&!~%;WP{`P=NBX&+xc{J@vyjr>i$k3?Mw4!zlXT4rVb(tVMFl+~@;;wn|Ab(@W9 z`)$7V&F^CFghP`;{+6#Ouzl5L+_)el3kaK4UGALPy->2XsUqXS_v%*~Lj~&DY2Sy& zz3_c@>f7%% zVU|u*#OTmj}^^+p`bY5bAL zebCe7M8pH9wQBBmsaDq`#rw9De;LKo7~$?cQ*-X#7tWJxzZTVO8k<;SaWtXzbkU2E zm1UDxu=1CmQ;cNf@68zOH{b8%`qeQZ-}?_>#pPy=6)e==H>^3ZQf33QkLq*P(nTu-A8!C6I)ye!!oHg=E{LMJ`4$t7bj7K@u z_Jm)Dv*C6VpH!rsANig*@ZYxbJ;?cpD~QF}wNh1Z0_S6ZsUR6=*GY4MHO6>lDM-ev zP+NgM#+ce$;EQ4B`U;+Na5wXt@;w(O(s?%6R>#%@5>JX>Rx?mQ%0#lOz!9f^p`YL$ zCNp!GK!jI|M+qo=EZQT67s)Zxy0K#(-9{9&+-qdV+@?R+1345 z`i~7f+%|%dUApPbyfr@l3$iZXT@d8-y;sWm`5O{rbPR8(@}@YAqi#kG4b|FV*5Z)D z3ms9mWYHzoam!Kai$r!O#;;%V$mwkLx!rHHtW9^t{+#B$bC+G~w&?z%yhWY;cH~uY z=1-g(*!NOF%H~Sj2fp{u)fU-QPHgK@CEeIpQ*bOIe`jFhwz#+?%Yn_`8!An+&LkDY z?sLfhVRiiA#nHbK)jU@3f41T0t3Kryt)iLyvXmWj9i;7_ro3;uwtn-=>cXcdo2#CD zef!q)NcPOh`SU$0MmeQ!ny_TwdCg`ohrfK@yir}W;MEBQ?NoU6mF2c=tZUX9N1YE&vsshfpv&aUe-oU=%(lrLJ55FP z{kr$T85;)7cKSKMEqKR%@#M!&`eQq$iv!obn{FO9(QiJr@-3(b3lqcdno5IcG-ey_2&` zmjyV#PH5ONCVGGEgwKyhrSq@V^)I*xVE~b)|{hxhb zynOQN`k+@Wfzzk4U-aq)+j1xt8z>QUf;4<#)-Y$6QB=_9ZdR-)Ufvg-cA5jbgj zxCk%fWN7gglADn8enKJc+BZXm%P?-_Xkie}^!;OmR(SOwMmQPM3(OG4;965UL)eZf z@5>b0;S_zmT9}W!cE&d0LOh$a?-SZ$Di$TeSj=tNN#R6uI$6QDr@}WVYo9j=opIJ0 zHVRo-P_y^KJErJuyP7B;6Umc{Lhu0i!bCI=i=1F7%EGI+b|P(TD-}7qfAuGMwy7W z4MXyLpDbR8Db1T9-iT|Z;ac%ZjDNU5oQ@lT**5WBysF+UeuOTuK=CDUJtC5OSxl|~ zBd>{hc)W~%Adaj;Zz~GbKC)3Q4>7;ec~Zz=jF*6PN>v@Sj)JwkZl_uWc4FZ{wOv?K z|08M%SgNBZ)ZStZww+ZA79ld4obSC^@60DxRMx)eGO3G>ARm(CE)0 zaY1Lj+3Kph_a5EyE}-&Etug1i>jL974I53TnPfT4@UbwAtF^IE@8#>(%W@h26$r@r z_1A$XdTOaN}EE99pV|EtYRv%Hon~0#`*G@Z_ z(auNjM0}gF?!)_-;G1`{gV)-*mCbni>U!^8cENTXcFpB?`gJTjv9Db4aCrTgrl(VX zd6yk*nl#l2+&0ladevA>r|{LgYe()cKa;;P*L`S-8$+x-Ol=<_>xZ3O7cL{Q>^)XS zj&~O($adnZ_W;A=tBKSvIai zaV$B$;4d@h%9$qUt(B$xB)$+>&kPy;30^uB^T2ZlmR^~vy<7#$Mmfq!xvqDSH{&qA z6(O(1!L~C>{sITqvKToz;p~l*YvQ*R@p3a9(C?PWC*yt5xpFOY#C_Hw`6OJ8nor3O zVvNGGavdC$<(K3}B=E1xcjDF38u>-MZ|FU_CJxVu&*a9qoa8pkKj3|{zsmPvJ6Zma z2jSI>UvdSOAdji|8?O{93NM_`;kpV(tZ$x?VlmEkElb5gyzjb=q8?M(@1PjqhopZs zQz63j|I1Rv6>Q`3&5Df}H)^NC3F8**QTX&j7|)+7GBGT+Q9%xx)0z}jSR=1D3TK?k z+20lQ7#7o^c!F2%U5ayf#bi-iFqQ2p6dQBfqe^W@Z$XfR@~4@B4HC)~EgnlKAG9!) zQh{hOSxO-lK)ICiLJ+o$8i*DiGHNhdES6DDXi*^}d-!rH6hWfp)Nr&oA}3g_a>@lk zdMXI_ISR@HLCz_tV6>1?)F8A7qbN7DD4@vZrr)n@)XAPn>O|&K>XbX$+@Vf|pv7kvq+4iUaYhw?_7zv&QO19ge+nYu*6Te?IZi5{UGp+_uMphqa* z*CTs0^$CkGePV=d`b6fY-w=BPf|X)GSe!K=x_>nw1|MQb7V8bEC?sLKA@TcQBdQOA z%r+wKzGy^viHwQxLyd{ScNkNCsHOhD??OL}iK#qHC|!1cO%9mUsL%w{Oel282UgJF zkGdSt<{(wjH<+l9fqDpLMH+%1CX@lcT72-%k;9iW8V*N}<{ZHbY8mjvEK8dM#21)uxoX5#6yez1?0dhKH=F zur2rejS2rRVt(@g(jJrnX!qbKz(Y7{7}ERw7Q7i+4EKJ33=Ng`SAkeF${uYBt{Bs$ z38nGiF&LUkn~s1IGpYyT{O=7bgJGexsVk7?tP3RO#4NVv&@BFuz|WjQcUT}90@$|W z;r-Egjy`CBvzjF>TwG@Elj~5paL8M#MA9OG!|+BX1f_|T#{ave&F)XBvy75yj(v@3 zB9W{bvnEJnBGgb%iO88$4k%3J=-YJnK;pg;p4m`WBRH1Hlu+tGNe>ErrUiGPO3WPa z5!w{FR?PzHptlNssa?g<1JxraRm8q{L(-NrG;27UDVt@_I!^wfP1+!jMud`rVz^-L z1y?PIRo>ArG!AgU&ti@Vu(qVonI1SHqK*gFeMBYsr8*5%EU@*49Yk7r| zz>d}`zrA~I45e*NyYcK*<12Y`7zfaY5~baieN;#~MW=!bW|RWO?8WK*-_`0c7z-@B zXVD9A!J4v0YBgI!!CQgQhB85a_q2h5IWiGEx1lHyV?)9<6GEAXzY-M5EduS`u4`^$^(DQkKX+r+i8)enS5+U@#~LLQSp;3G5cL(h#^eG=)hhSFx z7x2c8GQ;xOLz?q}jXjY!0{+dO$>E}yTxn04BdL8BzT9pKQ(=IpyBT}%xIo0?>VYIJ z!kO6E2wrFi@b$okCv+fVBgqD>~knOQOUc^ope@3pz}it}OF*SBLe^w0p_u?MAkPb0aBD z6eIr5d9qxn+%uTxwn{Oz6=ZfZdze-?}%%+6sJnQx=Fy2K<|SCXWjW^SHVoH;mMe z^BG<1pTIbN39Gqsme{Q00_i#~1>W^0M*a>-um&6g8jhq!IXOa_G3U78)^>pgNOhz% zkyD44?a>_xb8`;V9j3>hr--?`0{h=%IK>ACD!6=b%aIVThqPE;*T9e8#P$0?;xm47 z=^#`G!}?HYump(^%3h+I=Yts={v) zuw=l0f1V=d7#AK68_+qJ(nj_uwb9dmO^JO~2Z3fCsV;@N|HCFG@9vP6GXi>F>*6H{avXy;eRv z%KKd_$Hz}Vm#^K5dDzKynTIFBY%hi#S2^I$fb;>B2~x--Nb)`!DwOgci;a8+4h^7e zk$BGrKwGtfjsYYTjGSNy^=bjGPQ>3c>#7_bVIZc!KvW)e%E5dmQlt+;EVi|X4p@_2LI6Hg9c5$5jYq{ zQZ8eu_3fFEWbS{wrEAW|C0FE3X(0Ej)fOc@fdN(v-Kw1HHNeA}GDn++#)Vye2vy90 zW`(Kz=P6<~f(6c`)E#t&!hG`L<95u^r=%!0|NXeeU}*orq+`=P4rV8v*VZl+I+p=g z7f53M1U`zCsV_V9%pL29` zoRgpyxlju&B|40$pv{Hqfqbsx3emRB2K`-04H^vr?1lzDVAk_3z*bjc`w|*__6@Xs z6JH1Q5G3qj`{|J)Xm=OrPUVQmZ{-8eEdmNyxRJzgbAzlNd%n)^@ETCyVgcPYpMV-3W@^z3~ACpplYCB0L0OwyVGxXpf>) zkr4_W3a%c6L(3~@0od;TJVneEfnb#3et}H+s4VV@I)VxTu^Yns% z+-YE}7YU0e1rN?xz#NW&p`jcWN9ThbUL-7TKs2^&selem7y3JI_p%c=wjkgntm+wN z-Ro$-WrE**J}^B2cm@#dmtV43I|BM97cy5K>YV z8~HHU#NG2%@(N@6{gzN3{o%hqPZ9Iv4p8b(;m;Ab zX*ACg@WG$5M5itTtq~TEux3>Lr@dz!`t1`?J(ki&YKPwbP(8pL8rYM;fXVsiDPq>1 z0n-A=>2PBJrK{uKC}gCj&#;fp$mkZypc=ksTfcxFY!F%ldl%AYS9DF-Uky`53xF_G{!(@9EBcKE<2uba2xJI+4%mr{Xt`> zX%yU|+@2q(i@*hAN&$+aNcVIJf-G!%fQTS6=r5xI2W`NfAj%e5!}*lw+PARQ-hkAU zthf+;N!Jd`*h9YWbLz9Kq`Dd6la68!7ilk~e?fg+IVC(;J3 zLy68^G$BzOO~?sp)BEga+pi(8BmLc-zh#05w8V)tKyENW-WUv#hn)f^f=LB_P6K+M z2fugY;)6PljMt=}WL=7&)kv()+63iRiSj10N<527=)by!)dF z4IA*hV1!KcaL)SalVLa-K@&l{|9Og-)~z6WIJqk+q;addAjuAq4tO-2^r(JorkA9_ zfLIB`TG_tIDnKQK*w-cm3aF|Ee18MdXuttYuq=eMq`?X2qkLdoe1T=e=8p(;GN@*Dw==_5bJgEta|ZCxrZjN-eYe>*7_!*( zpXt@>FEJ2I5#wthR6`peGj`Q%F<3Q2tOI@wBPnqF+vL;XB#Mh?!HS z#;5+zLx7gdT=NvP+fL=#-GW6h33yw#4LF%20d4>v_bSJ3Vpx> zvqwP^syo&2iL+!BB||ay?-$hWb);htXc$FqGKMd9tx1Cwd=50XvZ>!4Q3Hn#su}z{ z23UDjKIsKLJMllB%|8y(!^jx0F$_AS^E5aSMy_e)XzI-1v zgj2nc8S>WK7XB({Fk06zyEXGWPk}=O$@<6$C=zdr1bb&?1euYS-}*dgH5A7Tj)=USjI$xVqVi1}RwMUjwa(0yPTNm(FjpF{VXw?i^h?YiGCUV8-6 zBZ=u((P*t-K?y`djc*dqMaK{t(H07#Z1BSG5|R?&`7jbK1$|7`<-44H`GQjO=ZtwaU1%D5=q5JT; zX%=XTCh;nbfgG+qz_@#BrE0d_ri-w^k0mynAiECOZR95DhFHk%b+8PS1j_V*cnI;xpI;Kdh)@=|VkGbxN1Ai$ zIB1^sQ6Og=IoOZr_KEPeLH6;|>}cqgVD&l_W9s63^Ol?jr% z+ctbag3&B^G8k{!J-;=RWWW1JXzY~a|Jg-c3;L|=Pum;@mGbf8+yZbgj(E5(4ys_k z6tu^YpfVT_RrtJIMn0zH$o}6wauR&UDqRT{k0&*^mdZSmxSwhAJ%W(&aUc*+3T z)M!#+_1{o6`f5k{1d?SV$IUy?QjV|Y5yHQ}Ip|)3oO{V}w85Vt2*An-MBc3vpu9=_ zH9t9R_5p-2OnMJ24IGaknl)=B+SMT^3xJXD_uo{e@4sk z;p1AATm)_<5R`iQ-Sar|PBM|)4OmZv`gctLLnc!CG4zlToH5G39cBXcpF*siDE}QC zJ}K$3Q~zgld@k>rLjM*t16tkUPobc@nnx}?P)}fMfhQBmIqE0P``8Mgl}PC$Q$BxF z9y<^oEjjS)pltinTNRA4#V(QdCKu^}f zQlfnC`OkSdICNG{fgg#)LV_e{A?lhO+`1&!10ADDPw(nk_%jt&2kC!44E4VaeRv)E zB`=AP*i4gXxF^Sl&FUnQr@9MX>OF_!ZvmX4mFsZPLpd0G53&n)A|!exL!*?x07H{0 zV`P-5cu(E|$bSxfg|1}L#x^;4ovWQv%0{`<4xT5IhA&KkEar9s zixg6ChC%@Q7E=LIQpiEfCpGe+JG5*HBnGR*p9kLAb3m_2u;SlHAzpkBk%ftT#qVxg zs7@}`tx_S?5(5R8sH?z3Gm>$^8LPiXL6;nb22n17FHOOURLTr#-QRVM=4>dy4Jd$e zl}WQv;52-lO0EolK}Ia&obTT>bgKHK_4)+fk6hLX_o^(A-DG)UA85>-Bg{*q9?3zO&6 a^xtP6`gfBIMl3UySs%w>6w2wls{aFfbr&Q6 delta 14881 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 diff --git a/elixir/lib/cli.ex b/elixir/lib/cli.ex index 034ade4..da60494 100644 --- a/elixir/lib/cli.ex +++ b/elixir/lib/cli.ex @@ -1,7 +1,9 @@ defmodule GithubRepoCloner.CLI do + alias GithubRepoCloner.Cloner + alias GithubRepoCloner.PageIterator + def main(args \\ []) do - args - |> List.first - |> GithubRepoCloner.Cloner.clone + username = List.first(args) + PageIterator.repeat(&Cloner.clone_page/1, %{username: username, page: 1}) end end diff --git a/elixir/lib/cloner.ex b/elixir/lib/cloner.ex index 9ab2f53..58c605f 100644 --- a/elixir/lib/cloner.ex +++ b/elixir/lib/cloner.ex @@ -2,19 +2,18 @@ defmodule GithubRepoCloner.Cloner do @http_client Application.compile_env(:github_repo_cloner, :http_client) @system Application.compile_env(:github_repo_cloner, :system) - def clone(nil), do: true + def clone_page(%{username: nil}), do: {:error, "No repositories found"} + def clone_page(%{page: nil}), do: {:error, "No repositories found"} - def clone(username) do + def clone_page(%{username: username, page: page}) do username - |> request_repo_info + |> request_repo_info(page) |> parse_response |> clone_repos(username) end - def clone, do: true - - defp request_repo_info(username) do - @http_client.get("https://api.github.com/users/#{username}/repos") + defp request_repo_info(username, page) do + @http_client.get("https://api.github.com/users/#{username}/repos?page=#{page}") end defp parse_response({:ok, %Tesla.Env{status: 200, body: body}}) do @@ -30,7 +29,11 @@ defmodule GithubRepoCloner.Cloner do |> run_command end + defp run_command("") do + {:error, "No repositories found"} + end + defp run_command(command) do - @system.cmd("sh", ["-c", command]) + {:ok, @system.cmd("sh", ["-c", command])} end end diff --git a/elixir/lib/page_iterator.ex b/elixir/lib/page_iterator.ex new file mode 100644 index 0000000..47bdaca --- /dev/null +++ b/elixir/lib/page_iterator.ex @@ -0,0 +1,12 @@ +defmodule GithubRepoCloner.PageIterator do + def repeat(function, arguments = %{page: page}) do + {status, _} = function.(%{arguments | page: page}) + repeat(function, arguments, status) + end + + def repeat(function, arguments = %{page: page}, :ok) do + repeat(function, %{arguments | page: page + 1}) + end + + def repeat(_function, %{page: page}, :error), do: {:ok, page} +end diff --git a/elixir/test/cloner_test.exs b/elixir/test/cloner_test.exs index 34fa0b6..8d98bab 100644 --- a/elixir/test/cloner_test.exs +++ b/elixir/test/cloner_test.exs @@ -3,8 +3,9 @@ defmodule GithubRepoCloner.ClonerTest do import Mox alias GithubRepoCloner.Cloner - test "clone\1: valid username with repos" do + test "clone_page\1: valid username with repos" do username = "murjax" + page = "2" clone_url1 = "https://github.com/murjax/spring_engine.git" clone_url2 = "https://github.com/murjax/burger_bot.git" name1 = "spring_engine" @@ -24,26 +25,26 @@ defmodule GithubRepoCloner.ClonerTest do {:ok, %Tesla.Env{status: 200, body: serialized_repo_info}} end) - result = Cloner.clone(username) + {:ok, result} = Cloner.clone_page(%{username: username, page: page}) assert ^result = expected_result end - test "clone\1: valid username without repos" do + test "clone_page\1: valid username without repos" do username = "murjax" - expected_result = {:ok, "Command executed: "} + page = "2" expect( GithubRepoCloner.Http.Mock, :get, fn _url -> {:ok, %Tesla.Env{status: 200, body: "[]"}} end) - result = Cloner.clone(username) - assert ^result = expected_result + {:error, result} = Cloner.clone_page(%{username: username, page: page}) + assert ^result = "No repositories found" end - test "clone\1: username not found" do + test "clone_page\1: username not found" do username = "foo" - expected_result = {:ok, "Command executed: "} + page = "2" response_body = %{"message" => "Not Found"} {_, serialized_response_body} = Poison.encode(response_body) @@ -52,17 +53,18 @@ defmodule GithubRepoCloner.ClonerTest do {:ok, %Tesla.Env{status: 404, body: serialized_response_body}} end) - result = Cloner.clone(username) - assert ^result = expected_result + {:error, result} = Cloner.clone_page(%{username: username, page: page}) + assert ^result = "No repositories found" end - test "clone\1: nil" do - result = Cloner.clone(nil) - assert ^result = true + test "clone_page/1: nil username" do + {:error, result} = Cloner.clone_page(%{username: nil, page: nil}) + assert ^result = "No repositories found" end - test "clone\0: valid noop" do - result = Cloner.clone - assert ^result = true + test "clone_page/1: nil page" do + username = "murjax" + {:error, result} = Cloner.clone_page(%{username: username, page: nil}) + assert ^result = "No repositories found" end end diff --git a/elixir/test/page_iterator_test.exs b/elixir/test/page_iterator_test.exs new file mode 100644 index 0000000..551fb79 --- /dev/null +++ b/elixir/test/page_iterator_test.exs @@ -0,0 +1,23 @@ +defmodule GithubRepoCloner.PageIteratorTest do + use ExUnit.Case + alias GithubRepoCloner.PageIterator + + test "repeat/3: Repeat function with next page until it returns an error" do + {:ok, page_reached} = PageIterator.repeat(&TestIteratable.runner/1, %{username: "murjax", page: 1}) + assert ^page_reached = 3 + end +end + +defmodule TestIteratable do + def runner(%{username: username, page: 1}) do + {:ok, username} + end + + def runner(%{username: username, page: 2}) do + {:ok, username} + end + + def runner(%{username: _, page: 3}) do + {:error, "Finished"} + end +end