From e7662616eadb96e428a22df7c7507a798adf9469 Mon Sep 17 00:00:00 2001 From: Jacek Weremko Date: Sun, 16 Jun 2024 19:54:19 +0200 Subject: [PATCH] Deliver simple, min and advanced implementations. Also test that proves each implementation works as expected --- Kotlin/README.md | 14 ++++- Kotlin/img_2.png | Bin 0 -> 62124 bytes Kotlin/src/main/kotlin/com/gildedrose/Item.kt | 1 + .../kotlin/com/gildedrose/ItemExtention.kt | 2 + .../com/gildedrose/TestcasesGenerator.kt | 10 ++-- .../kotlin/com/platinumrose/ItemConstant.kt | 9 +++ .../main/kotlin/com/platinumrose/ItemType.kt | 14 +++++ .../main/kotlin/com/platinumrose/Solution.kt | 8 +++ .../advance/AdvancePlatinumRose.kt | 23 ++++++++ .../advance/extention/ItemExtention.kt | 28 +++++++++ .../factory/QualityCalculatorFactory.kt | 9 +++ .../factory/QualityCalculatorFactoryImpl.kt | 22 ++++++++ .../platinumrose/advance/quality/AgedBrie.kt | 28 +++++++++ .../advance/quality/BackstagePasses.kt | 32 +++++++++++ .../advance/quality/QualityCalculator.kt | 11 ++++ .../advance/quality/RegularItem.kt | 28 +++++++++ .../platinumrose/advance/quality/Sulfuras.kt | 28 +++++++++ .../com/platinumrose/mid/MidPlatinumRose.kt | 36 ++++++++++++ .../mid/strategy/AgedBrieStrategy.kt | 25 +++++++++ .../mid/strategy/BackstagePassesStrategy.kt | 31 ++++++++++ .../mid/strategy/DefaultStrategy.kt | 25 +++++++++ .../mid/strategy/SulfurasStrategy.kt | 23 ++++++++ .../mid/strategy/UpdateQualityStrategy.kt | 9 +++ .../simple/SimplePlatinumRose.kt} | 29 +++++++--- .../kotlin/com/gildedrose/PlatinumRoseTest.kt | 53 ------------------ .../platinumrose/PlatinumRoseTestTemplate.kt | 46 +++++++++++++++ .../advance/AdvancePlatinumRoseTest.kt | 17 ++++++ .../QualityCalculatorFactoryImplTest.kt | 38 +++++++++++++ .../advance/quality/AgedBrieTest.kt | 38 +++++++++++++ .../advance/quality/BackstagePassesTest.kt | 51 +++++++++++++++++ .../advance/quality/RegularItemTest.kt | 38 +++++++++++++ .../advance/quality/SulfurasTest.kt | 38 +++++++++++++ .../platinumrose/mid/MidPlatinumRoseTest.kt | 18 ++++++ .../simple/SimplePlatinumRoseTest.kt | 17 ++++++ 34 files changed, 732 insertions(+), 67 deletions(-) create mode 100644 Kotlin/img_2.png create mode 100644 Kotlin/src/main/kotlin/com/gildedrose/ItemExtention.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/ItemConstant.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/ItemType.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/Solution.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/advance/AdvancePlatinumRose.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/advance/extention/ItemExtention.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/advance/factory/QualityCalculatorFactory.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/advance/factory/QualityCalculatorFactoryImpl.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/advance/quality/AgedBrie.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/advance/quality/BackstagePasses.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/advance/quality/QualityCalculator.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/advance/quality/RegularItem.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/advance/quality/Sulfuras.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/mid/MidPlatinumRose.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/AgedBrieStrategy.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/BackstagePassesStrategy.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/DefaultStrategy.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/SulfurasStrategy.kt create mode 100644 Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/UpdateQualityStrategy.kt rename Kotlin/src/main/kotlin/com/{gildedrose/PlatinumRose.kt => platinumrose/simple/SimplePlatinumRose.kt} (56%) delete mode 100644 Kotlin/src/test/kotlin/com/gildedrose/PlatinumRoseTest.kt create mode 100644 Kotlin/src/test/kotlin/com/platinumrose/PlatinumRoseTestTemplate.kt create mode 100644 Kotlin/src/test/kotlin/com/platinumrose/advance/AdvancePlatinumRoseTest.kt create mode 100644 Kotlin/src/test/kotlin/com/platinumrose/advance/factory/QualityCalculatorFactoryImplTest.kt create mode 100644 Kotlin/src/test/kotlin/com/platinumrose/advance/quality/AgedBrieTest.kt create mode 100644 Kotlin/src/test/kotlin/com/platinumrose/advance/quality/BackstagePassesTest.kt create mode 100644 Kotlin/src/test/kotlin/com/platinumrose/advance/quality/RegularItemTest.kt create mode 100644 Kotlin/src/test/kotlin/com/platinumrose/advance/quality/SulfurasTest.kt create mode 100644 Kotlin/src/test/kotlin/com/platinumrose/mid/MidPlatinumRoseTest.kt create mode 100644 Kotlin/src/test/kotlin/com/platinumrose/simple/SimplePlatinumRoseTest.kt diff --git a/Kotlin/README.md b/Kotlin/README.md index 0b80bad9..91ebb827 100644 --- a/Kotlin/README.md +++ b/Kotlin/README.md @@ -64,4 +64,16 @@ During refactor I also found README file in a main directory in which algorithm - quality cannot be negative At this point code is readable but not so extensible because all algos are in a single class which might grow to infinity. -The usual way to handle this situation is to go with OO approach - I cannot modify Item class so I will implement new one and its specifications. \ No newline at end of file +The usual way to handle this situation is to go with OO approach - I cannot modify Item class so I will implement new one and its specifications. + +### End result +I didn't modify gliderose in order to be able to execute regression tests that compare my new implementation results with original. +I created a package platinumrose with few subpackages +- simple: is the simpleness, cleaned up implementation, no OO concepts or design-patterns +- mid: is the mid-level implementation with a separate strategies for each type of item +- advanced - is one of possible implementation of production grade code, it involves item extension methods, qualityCalculator factory, and separation of concepts. This is the only impl fuully covered by tests +![img_2.png](img_2.png) + + +### TODO +- I didn't cover extra case described in the main README because of other responsibilities and lack of time \ No newline at end of file diff --git a/Kotlin/img_2.png b/Kotlin/img_2.png new file mode 100644 index 0000000000000000000000000000000000000000..6266b570f32f976fbc6e5d8ec0be98f45cc0a370 GIT binary patch literal 62124 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�G9r+`@|%#mVrAqXeN?@vd`h=wSQC6N;I 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

5PN-VfBpmm12pWwx6 zmFw{oGrUenbWg~Z94*Smq#D4A2^`=Nu=(6!*CzPjhtFxn9}Hk%n}$o^de~4;I}NdS z@_Vesa|YlcPHFW7U$qWR4*GdX@vG;-jmN*C=TO(?pKQ3@puBPH)e=HAVY303(!F;H%QCFB94~Bk0>7Q{lMq>c@PdV&SaZ>3^Ut>juAs|;zI07IPxnA$3O5XA zJ6@7H+R0uo8aBzwHm4<@})M*v)1^e^Wh9tHqAp(Vj8W&Fx4U zD1_tm`=>0x9V3lssKLuA$#(G8e!5bR&shppr(y||3QTgIHacJS40-+M1u)|hM|1Ni zTrT`dmoLqn0}iT%VP6$#(%AwpE03$XiwR+~Yw3a2+0*H6OL?9FuzhiC0{Gk|8hbz4 z<>`O%WXe80!u7_=IP?<4(27DlM4jv}P`ERPLcC(>?;|g|f`52mKbM5WaHlhm=EuBa z6=lSoVB^N(MomC0>(EhpP*q|tnfbo5YnVtwPvp|y_Wq?~PfIX?&2vj3XpnCU>K!e= z9o#9cwYX_$ud%y@(qaQ_?j(_|DxmfN{M;ZBp>Pvqz_x=h9ogx0`^thC3xFSH@ye|; z=F>IFvvID+}qpZ|HwM#-A9 z{801{fIG6t`=MIE%;9t7OwuX@KS*`l?rq~(zTxqn7KO^bMy*F4RQ{}vz)*XLDaiB_ zyjMpCct*9q8!nhyK<1ca2e1b?Tin>bMpumlBXN!C>op^V(|1e6t6hFhjx5EJ@<*or zZR5|IT=YqQ52-{J?mW+*&rZW)34V1F{Pq(WYt zUT?RVd!^RsYq`q%q#lD5~fk`oX;p%b|(j^OEE*F(D(Zgb%NIn75IM-=gM^!pnHQR@ZGDj*4kQA-#FUxgw<(4C6Q{v;BB+?2p9C=e8c=# z{{(#kT|$rtcJXV$^5jO%?O(AN#SU`ay zo!87{A+bH)`I9;V82_I@E&lwHlRsWVa+UudvygX;@4q9XG;5Eu)%)StYLW3oi=O$H zTQ}_L#1=Nwx*LOXUL(YP^9NVrJX4$|abcqb1!B+S~c z19Xwz+_g|3lKFE`?pF_J$lmyxNc)fR9<+LO0j@s%F`LW-D4#<57)`NWe4MY7mq*NGt+IFAEkq)1r{ zG6Kx)YZow(Ewj-q5$cNa`xB*h`mo9szsDqDtWUl4D*JVW^b}tJ*oDiNWVuxFLONw? z08PETJi96~<5_dq9ktn=zNaYsrJ>5GHMf^OVv^7LdWcX-i;sLvX;1GOnxS##xTlGN zx%FH*)HjUf4T!Qj_>+P>h+7ACbIEH-8+1qU(W+xY@Jipe^zVJ34(<~{=x({)=x}jr zAXcdLdwncSE8_d^12C@_dXdt#KBDgE2(<)KAxzw`rB*{)1(UCjJ*F~fQ2z=2YT^!? zwj+;J;Py&!YAE#-O1-3^~>Jb^^||=@2$Q_kH61t zDTJqve5MU&6kHk7XM+J?UwxxR!I7~_n>g;pfqw$hFHHb{j00)XU zUO|!klfRPEvX<_%I1LWMzGM89m`ITR z0sJy7D0_8abKZmBGsb)Hi~LdNZz9aw!NKKcA?~Yr#x?Z5W>__{!-d?6#j_XQuEg+0WJyGzZ{^k^d~ zF@2ec#g&F&8csV8VJBvYB?ucIN2fe|js9NwFea=3`?Zh(a;m;}X|{r~K6ApC1%%IoWuV+O@;(IYQon;vZ>#o!>YvD%R9;7r zXVxE#(hLAY@;}xrdhq%)yTi8N(v;V8YaS)+M^`_gVKidXU(W-$C->W(u@~+&3m(@- z5>x_S;7?y2&#~#*xHUAXswpgAnsrNQSC38(%AWC|P^Hwq3;BHVHz9q2dgkvj(|F8KVY() zzIcj(J`Us$kZB_?z2F9h*M45h0y@z1n~K#t-Ad7=<$I*)00Po=U%w46?$Fi~Gg1I& z>UsCA4BqM3#DHze+Y<%rQAxf#*B;=nDLzQUiw#znCyW5(wgPDlgKsxl9yWbejIPCB zs{1$|q*ymFL@}9ie=YG>2~%nnb%LQ^0m$v_rU41x+xP@Bg;a5oh8^RY(_`WSVq?`f zYyj(>ZE+!#O4GB2RX6M8!6mJK9~C++$|e^oZ5$2}IZ_!dJA5-6l8h?r_a_AMpk-XY zXaSqA9JBy*EpsOT5?*GZDUUUP1(j-&czr(%rKw{Sp zb9yG&5*|A59>gsCG{CEkOGk*g%h@-7jcvO=GmZ|##S{){oA*Ic{`+6-7sdKwK|+Vxhu3+RpN0o7A(D|d$4)&O(h9u zLf6#2EZ1NtapNpg9ro-E9>5}NZzsh-+0U6!_Nfusq`5-jiK=3u6{QeJQXoB}_&x~& z9Y;VJ1noVtmbt$09Mxw)*_joNgszQgG3+4`>Pu{kkqGuUYQ2^KhN9-ZR}D^pcJ++iL($A;nU;b)kPF~6 z{z{n5@$jpMNBewe7mG3=1wqDZBE;e!SVQ>l!Rr_abSO3X^8%Q6e#8paOv4552_LMD zT#xY9sf)SY)Jqn*P_&Ym5lp{8yy?eE{aFMUJg$7(AJKF>sj;3Gyo1iZPKM5pXD%fU z(1sS5QQoxj-ZvP()FHb!3}(fF1JksZ&cE}!^zm|c6XAk3nj#Y8-pcQCS0f`rarIm* z;szE!m9tk^#1E|CNSK=0SFB>zdF_*Dcrp zY>rUOr#nA|3!yP4O%X9$Ie!<)7Mtp){BCme>&;Vv^CN`$E3wC9V)y$YuR5aO?fVly zTqX>X19@3u_t8^)VB>VTTDvelre!*dBVGT<@Oq^Dhq(#LmIN)H{~n*RzkLyPoYZeT zG$CFwG6$wFH0Eqo1)F`)F2m}9L_CXm0ZKdJ!v;$T$nYQgvM!|jfIK1-X{6T^dDrBE zZ{J|-Bh!C3duQLb2%OW#hQ-d%8oux_Q3W(x^iceq5arJtkN=&{PKKkb zCVfBOkw^Z={P+J$2Fj53#>e>dLjrid%r)Qr5wNd*X{71Cw6+C>x3|WaTs#RDc zxC;SGn``?a69#1U{2RdkiLg^X6u~`vJ&OfGQ?s!?)#KazB-g;lQ7otdWH1+Jn>KP! zb`w0RsdofYUj8E9H-OPs&t!-$e-`qXridE`!fYVxm^XCQS9t9&0ZrVVB?Uf5Yh1|Z zn%4-VZ(hv@X`k`}=6;-CfVel755kV~SVU;< z@v9;c+hchSz?n(=I0=wJB2!0xLTq6;5Y>W022)un07}#1doOO(cf2kxM;?do`mvAz z=W4}*zmmc#BpT`)7(FG9CkMg{W8!%KElEb=p_`1z>qJQs&{X?cH%8fGvB^MZP!uEX}->tfu^3miy92|EJ_z z#AR*^2e~Jhmr6XdH8mg0gq+RX$C~))Oj4PEQw6_Y#}>ab?%J(3Q>nwfluR_?OD->7 zKc9%a9Z!Lr$M&S0-NeqxW`73G$yFlUe}Q>6Pv^gjcU8Z~b2-x}xx<1lwfs}MD_TCf zCcIz1oI$Z2NE-#6(Y`E!=aJP+=!hBYDxfe8xS8*+UY*^dhMLi3) zq7tN=(<&(8B&`Ji!ktC)Zfo$8dA6o7+2VkH-D`kvu)#ST#G*mHT31?kYa9h(B(s8KaMsDy>yO)3}j5MbE0KMep&w7o)%E zy@#_V*o`!$nO~S*C1CpA>vBwC{!6Gm5q!zNd09U`2qe44j@{^ZZwP$31eDT^mGZ)~ zK;kC-`$LF6-VOgkB7SAXP*!Cssi&(!2Jx6XwGd%TWepJQ`Z`X%zbGIP>@zL8ng4{? zE4;NNtrk}k4ahn4>piALTrEy(uT3r3%E4I}-_Rv_S4U;{X+UGJunlb-^GZ_g`Ru;{ z@==8tX8Js}C-EfLhOsl#Z~S7UPTArQIgH~++Qh^W*;a&5ZynCws&cSfQSs8LK7%xH zJ9tI{Qe42^dAAR1TOO9oGh#iA+?FL2Awia>kSGEdm&jrb1E;&a7bedmMg%u1SaG+W z*&U?lBdK+Ey;cE@k3(sKah>lF#V6!`{EiC4Ztm8{vmh`4yU9VV6;WDZ9qW zD?zXB;CE7Lv+ZAj_EIxBHhg?NlfJc@KKsh?n`=k6Wx`cq@0!qUOMlOY{FFpZ zJ`*PvYdtRSHF-ltYj`~Wt9_C0<>CSYS#}hAZ&(PSj_m9~Kq4%C!a%^KGLlR;hPEyL zOHdMcp35x%AA)}r)VFrJB{H6?Shy%40!rkq2Qr9OG++GiPr2;;)7LvNEm{j-DA6^T9`uCkoQWR(T5w|xJh+JUc{F~n3)Q|Y~ z@50Y2azg%A%C~lu>c1*N>Ms4ig`hWVd}k4NTy4<}%m(1T`2gmNM9l&^Qrnr==HkJC zWmIZ@f&V|UjIJ885CfJ`KQpinF%3ug#q)JEz%XhpBoPN*@4<`#XW38z>J&L%^dMm# zI~^*fZwq2Z-GQjK_02NAq6jWD3`o{=E>|v?SZ}?1pDa5Dh(d73=LsJ))IXQwXnFkA zAX2gqJ~jLqw?nz0C3Y$NBN-6FSPL667R%&^=i?J={biwdTUvX5o(ZgxD+_XUE`2A? zVt}?8{Ztyzb7dT9)jX7aFo=jea$}0;zcu#k&S83++?y?ae=U-d0d+5rzOR!@NJ!tT z`76L&+Gb9i%>0pE$MpU_!93khScU#UFmzUFSzSu zXi?8BD9dNV0`n-^F6vCoH86rx3c z7hcOx7gR1a60bhuStY43|Dr2iH`JGM{yfb=%z{bmVDjV{w+Qz>-^Sbh`uc*u$h&7v zg!hOwk-+)$gZ*vqY!LfWl0MJV%b)Qey#T0yJM-pzhN8-%I=?BuZt}dnh2I`YP3SF> z;d(W=A-(yT;c(-7ya$@p`NT+=bTuzB(w;LCSYg zG#vY!s`HM6gAL*cK%ZI1>AasjSf?WrQ^j5xvUsdMRzK9ohg!5(gu$nC3D~eA(fhqW zv+GB1oajhuPovq;>%bj1C28p@G7Zl`u;#!0iq@S@h}>Axu17La^cH!&ATXyoo3B!= zKA*_tw?u+}Ti4A5#)gPk@qkBL!4WuU{vKukEgxR$&;M(wPUKgJGA_3r>PI>_DQx|J z^0V@1Ap^SZe*c@y2fIr9dK3*My8@WUnus+b*YpJXsEX2ATPC-;NGvMluvdPGd{h!1 z27%PoGvDX>FgYVWTqhbdzJFz(#ltB_v_lwy{Oe85i|&V-#*coNiA&!NW~_c20UbCPh5dbGzH`OW6KZ=S4l#xXuB)K?{q*^}Ew&KRAo3G@ z-he^#KAs_71khoeM=B`i9VncSoK?BGs}-TtY=}^jrlU(z{vQ2UshWB%Hb5qaxl^wu za&Ti9iCK46%-g)z$W>p6J>IXwcQSNSq$^PGUYl@K-8e>$6;6_WU2Hf50a6iBURf>X zn(`pOA_LkBNUF=xqhW$_6rC8^#nFw)#~EPr=@}dm+6wT*XR#O?F+T4<4T8=a)s20@ z+}n5@=!ZE|=)ic%iAd<9-vOJ)2QBCOSL*w^ru!mY&r71X24>^=>!l(dg-Wl6gIaI3 zx!g$L%j*pZdhF9q^Tso#u_eiPp2v67qRj1%V-NZrGrmIctGC_7x+5YrU_b6Rp(GW@ za-(YkBB)9RZk{T|LPPv1IJJA;65p^d@$Wr)@gfG$qSGep?mn>*=^gOJxyoQW+_@7) zum*6apz!)uhqu@u`+N%t-T#y;<*UAC;RCQfhn&wR>4L7a{!?@9cl0(F#5dpA|HCLG zl<2@<;d``?|MZ3AE(a&wfJL5-#25MZCqR)k|0tF zcrB^yHIYIkwJt(__IDfl)N2y&&UJnbfI=T|sN7^01b=?q+Ol{j5XuDFCs8{=gJ9J@o zmE#p2a}YS<30*lJV)t>7I%hq`D$%KuwdfbreElB6_c>?O7w8JTZxzF-Y|A}(6@6&M zot?ivLftE+#ox*{MN&yq?B27tz~b(w1#ki;{dc6D%xS(YtMqbeww0K0tW}h^kF&gX z>PS;+Dx53w2D<%vs2YH)2o=EW{l$3|P_bF-3BPteF`cPx91GrJQdRWaeyP*Z(Ox^- z|CAV~czd$kWo}!~$yyHsP#%mj5YVAKAJ&0{u;D_>4AudS7huM{e*}z=zeey7dBVR0 zYdNFH0s%=TauHe5rtUm!V&%7rkb*=$7C$J}!<%qvYv(|GQ@h?SH;E6lecG9#fVR#5 z+snr;ZUacvvKc_C8#?*nYi#e2qY7Cz$IrNH)hD}`PWYs#YU}@cPHsQH)Bdb)b5pv; z;_h!@NN*YI&nju1{R^R8r6sh^GhX$rzv3}S0I`-=n>g2pQ`ccOIo^c1a>G~7lt2=a zC&pb8l6!@@`AaT7q8je9xH#?RDXQHM@hJ-#NQF=5AIlbf`B~xCwG2D-7+-9DOrF<% zt|oP%wVS?hPrz?9j#GS;7k@%~@lhV9|GcnzrzjpKK_0H$o8U({dlid3#PfWb-$d&^z0_nv0~nhkdJo0InAw>Wz_;~pqIIh?KW;qSVcRU?jG_N-=(Ti% zrwGOSb3-qijZuFXy2EpB(gNB%#j|0V5oB9t&wf8u!a|VlPqx<>hfzfHfyZy-i718QfYR?%riNB7@-LvI}@TB zKyStH=oERr3^$9zL{Ku5;4La`HJfKO23;6eAw-#^d0alFbbJ>t>b+CZIHV6z=PRqt zUSMXRe53T+FKMB-Jl_eh)p3&4&&pD}>kpq5Sa&0#-r zF29g!`XgiMmkEMVfe+Z!N~Ec*zp?+r$aCglst&!VNH^G9a&G zx_kURuLT;Xdf~^t=Gp9m_^t2V1BrgBUOg7+6TwV(W8}T?w!ulA3*6Xd4^OHf8OsR+ zKNkJ!%ym95d@ zRsE{}#;fZ}qtT)mVCg<+ttZVRkQh{Toxrf7v80)e#ASNjl1SX(N^Kh_0W>|}dtUB0 zAqj5{hSazzFiCze#k;50H%A5=rh&R(uk{V;Aafai@oGIXG#=J~)2uI|k_vOQ#->wD zqG5mIFi6DVJL8qJR{YyF1e?eWZ)1*~ck;yapbgP`;s|m^$=|Lb{yND1J-;azq8`c| zQwcbaLo@Z`o73mxp^^7AI^s!`c-wC=y)M4L$Mgakm4X4X7#U%82hrxZ`N!@idT24l z+A(SjiQwiKB?+x9)&Vi&P*FEO3&BjBKbvN-OLGA(#rXD;O6@J z7wNw}A&xxCjr|Q>N8;Y2YZkxn3uzyi3v3%3BQJ0nuXPa{*f7M+omUL=T;#KYBan^* z1t)2qmCq^uzPk*>bOwTueewv0hTyv0RYuVzja}4!QpB4 zjbIaJ)>##{B9;Qt@%aG6PVMfd43&+xj@GHl5(?41tc>xf6WyG z{#i}CK8A&4^2+}ZstJq%l_tB1Y|o;avL@Bwfc>y=xkEdkVZ9|maX1{B%SK!IR@_?L z-Z;=niDi1GMjay7=y+oFw`3>=qklru zzb8XcY{MdKy!~K@^OlZt8d(!0EjJ4y*>D2au$`HmN?J8n@cs>t_N~+kB5RKdBmJ{b zObl%)LS-MUobii!&Yv95sdQ;{JLDq}i(LshzZgC7Lng|bk(3xN4S;_K-nzrNP%s7V8rlZe0i9^SG?PEDs2O^kNbSzcoX}mE^ULO4k9#l6k(clUcXddE-oW*|K8owV_t#GH_;n`ExbVyKblD z^CMzP&-P#WrA@S6hn;WXiC9nNmU2X^1XJ^&xVK|<)3@ix$p_s-M%+(&`sClh?;US-cB~Gz=E}iUoUUcc5_zg6! z8KuHzr|qt!S9Uh9bjEj6t)#D^>QO9wNON5Uty`d)Z ze9UhymZi}GyrE2wy(oO*+g6n_&(nl8p(2ame$UPh_sq*~5%0^ly~g#fb4L_{w!9vY z^Q6-qD}zvKPvwy>)v_7UyM+u^xeV1JYa&9zJBadJ<6OLd@cDW-hu!p>rc^TJ9nRBk~PoqVy^{?UDV@jt`N5{ke;FrH7fPK;picM^2 zSrBP-uEd!y6&%I`L@NP-J{W8$+@FGlw@hW|kqd9)!Jh?630jczL(#=fJ8pg z%M}uQy3l`7x8}@q-W%+2RqYlB)N(!tl7f zDt;QTNYa){IFFvK_thwsW}^;nmyRw zqqw@_nyWdCwbs4xpTeGYT?tHTzEEoRS_u@XJKE26_Fk3NtG_;WOq#x(C7ywGL_?K! zQR#x}9A<=b89BB;eXF=_h+oUUdx!_4+wFMboZ%!oR$WICi+%*#WCV}eo*bK2o3Vbm z*dt5Y{FWC;xBS`r<7Im30aD%iVK^Iw9VSXzTO6Ab^EVZ{#$=`DT&|dj4WrVye9 z`z`0J@bVz*XUx|n1sjcgMQRY2?F=pJn{bnx`PJSGmw9`oY1pseU0#>XN%_*6`GF?a zACl4DXX^uA$AMjn6u14vXLGl=#NM!b?JJE3aaZ>tl<(qr`JkH?2-^>>#q<>Grl6n+^?4# z6ELZS%2cltAdsf_A4GDxulg<2D_?Qr!iQ!akrfe-k+htM7L+I{`uqVbk-@ zguyQ|2r2isD~oovK3W#T21Rt!zPZq=wl7=zcQ;UHs3U0&Z|phruJex3xw^Ube+w{| z*EHqP#X)kOp{LwIe=XF&Co*fV*r+&>*UMwE5%2KxL%6y{evJfYnXYs`NU;R>xIaHmPBrqcp}Ezb5A@j)^56WPz)rgbirLu1DBIqPbXhD zt6QTQg@1{P5fu7q8IC7zvp?^nl?cwMr}wLVb~EiMabU6~c2}RA&P=PllFLyV{xz@=I4$Ggw=DpNefu8`ntoMn(^f1t5 z!$R3gfsv$pvuMFgx!!>Kk~Qc@xI=|Zs)x`|JmPI&8#`%D-`0I7P+6t-t_-{C@Ni2n z-T3m%^UCw(?7OeiZN}ov-(Ce7sh8zecFZTjs3FiCsV$^sf$VteXn1?;&bTKlw8 ztTRRhk!%tr{!k))Uu--u??22!oc70le z|2)c`r`qEbaZT@L_XzuX!o=g4aDdNgzP&Zx#H%3Nh=Z&wY{?&5bYtUwcDLt1YB@c88ldH|Lbmw{xf>u=VbCpo2Wh^q#_Oa;(yY$PhP<<+ycLqcFdL6tJ8C zZIoJg(j_#b0*Xsvvw$bwVA6iZte~KzqnRvI9ih-XL8U$H47_)4)?d%*Agl24 zZmq{sq08BYX%CbcIxXvr?jCvo)-(ItLqD|S48$n?dN4N~N2k5vJ0E^I)6r5YduXo- zDe#t~sa>gW_-wF{pxZq1G((79$4iaw(Q9#dQwm50%P>1j-rUfJN4M7V4S^R;lg1P` z*baxrOEqQ1M#e@0`oP2c(_7bV5-K&rI$;+}?xmOAjcS3gT&N*1ZooXQQVzY}p9fb9 z!*3EG;eqBbx9?3!yW;a&n;=B(YT;CWV6n^0k$k3uU8gTeL@`6<>M$gVF3629 z%c~_&9yGMYrqp(hEJ07(!1l<^`QUTWC~P3)AbJRP_)>`fwolBZ?0JLtb>>S60b0j^ z{qwJ|S2WBjqd+2Bd0&O@WankR4y^uNxHF`KyN@7TL`g+0{gRTk$Jz^HSI)E!l+P|nPZeQTL?hZZ-7-QxTv^o5w)Vy8Y?0B4WWqkp~v4)L$^6WM* z4WhDoAIUenb+|Wqk90k&z8L?992TQZ4;S_~vJ@*MLbE4%g)MF#;Vm#mVzMr1 zU}X|E{dh;1RbG1=-Q<^jUZFf&{_=`fykb6D`1_z&%Lm{%7?S&1=2GnWsNDf}GW={D zHu<&r!+lQ_2S9U*TEb$tC*K$Y_ls|vl(v-Cj4ZZ4?)u_{CWJTs*11qcBfI>=k^B!% z)R@wE-Cw;=Eo=8Fw74taQypw+ z;YDy&m~x2i=37Q^hC-vV{|#^f^NrVGqky9JIjUOY=`Xd6v*Iu0{AUk|_dGZ0Vb>e+ zEU7!Io&)at(1rcb1-;4Y`ars%<5!kOlOiI=63K>72L1rW-F@guFMX!RVnrrwIW|*B z*a~OPc|bt#T#Mei#_Wqx<;`M#^vzbhNO)$0{dC8mj+=Rl&!ij2ALHH8c-Y~QfOXC4 zK`>F%S@atoVmVjD2`DPhNHovfJ47-wv_A#clqd@d zUljDi)2lL*LTh9mR9+2L66#4keVsecDZVx7f3wMPRpi*2SinBPRp|HT7w$u|mDbAJ z>QDZ~Xa}0G2Rk;NKF`WF3}O@B5U}5nWd20*>2t>vrFlKAWjs69;*s{2_CfiFC*E#E z>9OCmEvJNC$WgjO0}W@|MWvl8NUh>E8r@glHi@hOtHLm`d~4C&4!U#po0G6R3L*WS{WW%h#g zJA38zxn?Gxzx!@36X!eQm2K>6QC9F&TR#|KHKwx^iZTk~?h8mn-@^Owt42sr@|79{ z9gDo7CqQ#k5mEDP2e;O|wJ z|Fv`GK}l$P9KY?0+HQecr72xaMFo-!E0t1F)HF?z+!94XO-K#3EVrk)UCRYTD{*<| zR$^F~^=^nuu7F}nQ0h%Z(r%&j)apKZGk50Ao%`qe=gqt4pWi>{kMljhWj<%VGbhFG za})M@tGHmQX^+&3rjxJZyZ3A+sl z48&zGT@FyIru5hyo{i)~5D4^9UQ>yMU_4V*{wI0Q-$?;;|zl> z?%?bUIQZD$&+A)I57?Gx1h9b3MOz7@rA%bkq2e zr#zr;4PmD_3)#M9`od_5hwqFo{+lMgD|=mWu9!5obl$}^vcDu&`8c_mB>Z|vu749wESwy{>c$XTWC!M#5@-b!1RKokkREA47A} zmZ4v_G=?g5)Hj?WTNZL*)2Pw1op*AE-cL|~JTD@7Kzt49+2sPtkQKofjrKBkFi5lU zNsw3VyX=k4lF}zffe~tv``R&UY8p77U)r8MV@J2NliasVY zNRR`{LrbIWGwyr?DD&fuHdP+WTVio0PLrWot*!lDxTVENE~Plqb_}Ew_7W(i^++@{ z%$xnR;}Q%_qC_n*i3OmrTAAuOA$Wvehs9C{!Kgcnle;j4ngb6$UxFR$o^!tJDt_pof1SIC)&_5S7DwBP-CN3^}ZV ztc&{YlskZi8FlmGjE1EABZ>(I|J<|T0&U46c&H(ZBq*8ZYA!CsZypD@U2DdEvwZ0l z>}YOG)GVGxVBB4|yiC=_0Sc0>R3$4!B}=>dzOW8J#beUE z+imHVXmq^hXRE20^W34<$(v%&QoBFHvJuE@EnwqGkt!TeF z#k#M(6!FO+_lpcko}&LY5+&G>j0Jd26w9iH5y!IZ!O5WkqoN*HK)=LJ?;K7sn)Cc6 z-u?dR2bLXEF|Q6mB>K|(A*#M=@f_xb;cP15Z^kWr9(3&3mc&oUL|>cNZ%ffK%iWDU zk!9l(So=kGiX4n@9FW?Evo`f`wt3!+MW`>Tdt42DTOpNz8D>}8#kN&qP_LZqmFes&9=%D0tOQDHP-N`UJUv%ubt0;5B|)Zi_Vz=q zH*j5JnlpNdm#KTFC$u^oTfh$baRgrP8gE$Cmja?Prq;lpdKP^K1s+dSyu?x!lGPX2Z zIq5HnSWSkmZ@iNKI$Q^5BM-V8<`>Z?yVVlcwYwnaoqk)xr`rI!$k@~{o$TNpoP4zp zDjEb(PAgupO(MBfbzoMFd27Cj%L0iyuYFy90hM|1u)hfMG;L|1MsR z&bdDz9dmO=-Ke4=5X+i5f_-p%A_K37pl=kZ`lS$BP9ujX7$F>-_+A^}r6$V$*+~CZ zIShwuMl6{3Z0oBEs(8I;sE8;2cwfhRWAIb^DGQs@vPK)`BhL=msIC8uRojQrRUd(_ zVBhgP+YWvA>v1w#fZ4}s&&HGVsk5SebaaAN?jn{fV$Ei^yi-lKO{Q`mrt%(zzF&EO>A6s zhlm20bx*RxkaG+>f72@&X=9#|e`pU-nHI47)ZkjBJ3Q4DmfO2Et)B!bTdKB#A7Jy& z2V7~jgDl)Oh#Fan*q$TbSv6+T!!FlzgOpZD5<7Jux?wM=v*&yFJe@M}4Z<)4&cf6X zQetkoHSl5S_E$~`g7hZP@Q!DuT)K@_7;*awN{Fvy0iks8U0MFQR3pmd#l&#iz%%nI zY3zkPk_`C~l{BptpLn42%7A~Y&EB8V`3JIj1ySraztPRv1@~Z^Hm~2=S9k(J{hb+n zQ+}x*dz+|I=Qw}-z1Pv#Ml4F)BNTY|1>t9i8Sq*`LX3ih^ zmqiKrO8dC)1*_TH9Pbe+u{ z5`8C`{|1HspjyDg7XKRoQVdu?B%S@;xKcS-4G9FZe;>XNnYl(KqFd#9{T2SYg8z>y bc>CKrB^z_FYNz7)_Xc)Cq8%FT0&o5cq1(Bs literal 0 HcmV?d00001 diff --git a/Kotlin/src/main/kotlin/com/gildedrose/Item.kt b/Kotlin/src/main/kotlin/com/gildedrose/Item.kt index 5059f949..c54f9c91 100644 --- a/Kotlin/src/main/kotlin/com/gildedrose/Item.kt +++ b/Kotlin/src/main/kotlin/com/gildedrose/Item.kt @@ -1,5 +1,6 @@ package com.gildedrose +// NOTE: changes to data class to make comparison (asserts) easier. If rough goblin is against it, I'm ready to die data class Item(var name: String, var sellIn: Int, var quality: Int) { override fun toString(): String { return this.name + ", " + this.sellIn + ", " + this.quality diff --git a/Kotlin/src/main/kotlin/com/gildedrose/ItemExtention.kt b/Kotlin/src/main/kotlin/com/gildedrose/ItemExtention.kt new file mode 100644 index 00000000..88897af0 --- /dev/null +++ b/Kotlin/src/main/kotlin/com/gildedrose/ItemExtention.kt @@ -0,0 +1,2 @@ +package com.gildedrose + diff --git a/Kotlin/src/main/kotlin/com/gildedrose/TestcasesGenerator.kt b/Kotlin/src/main/kotlin/com/gildedrose/TestcasesGenerator.kt index b113f6ba..0d88df4e 100644 --- a/Kotlin/src/main/kotlin/com/gildedrose/TestcasesGenerator.kt +++ b/Kotlin/src/main/kotlin/com/gildedrose/TestcasesGenerator.kt @@ -1,8 +1,6 @@ package com.gildedrose -import com.gildedrose.PlatinumRose.Companion.AGED_BRIE -import com.gildedrose.PlatinumRose.Companion.BACKSTAGE_PASSES -import com.gildedrose.PlatinumRose.Companion.LULFURAS_HAND_OF_RAGNAROK + import java.util.* @@ -20,9 +18,9 @@ fun generateTestCasesInRanger(names: List, sellInRange: IntRange, qualit fun main(args: Array) { val names = listOf( - AGED_BRIE, - BACKSTAGE_PASSES, - LULFURAS_HAND_OF_RAGNAROK, + com.platinumrose.ItemType.AGED_BRIE.name, + com.platinumrose.ItemType.BACKSTAGE_PASSES.name, + com.platinumrose.ItemType.SULFURAS.name, "new none-existing on code name" ) val sellInRange = -100..100 diff --git a/Kotlin/src/main/kotlin/com/platinumrose/ItemConstant.kt b/Kotlin/src/main/kotlin/com/platinumrose/ItemConstant.kt new file mode 100644 index 00000000..2d2d908c --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/ItemConstant.kt @@ -0,0 +1,9 @@ +package com.platinumrose + +class ItemConstant { + companion object { + const val MIN_QUALITY = 0 + const val REGULAR_ITEM_MAX_QUALITY = 50 + const val LEGENDARY_ITEM_MAX_QUALITY = 80 + } +} \ No newline at end of file diff --git a/Kotlin/src/main/kotlin/com/platinumrose/ItemType.kt b/Kotlin/src/main/kotlin/com/platinumrose/ItemType.kt new file mode 100644 index 00000000..f168092f --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/ItemType.kt @@ -0,0 +1,14 @@ +package com.platinumrose + +enum class ItemType(val value: String? = null) { + AGED_BRIE("Aged Brie"), + BACKSTAGE_PASSES("Backstage passes to a TAFKAL80ETC concert"), + SULFURAS("Sulfuras, Hand of Ragnaros"), + REGULAR; + + companion object { + fun fromValue(value: String): com.platinumrose.ItemType { + return entries.find { it.value == value } ?: com.platinumrose.ItemType.REGULAR + } + } +} \ No newline at end of file diff --git a/Kotlin/src/main/kotlin/com/platinumrose/Solution.kt b/Kotlin/src/main/kotlin/com/platinumrose/Solution.kt new file mode 100644 index 00000000..dd34afdd --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/Solution.kt @@ -0,0 +1,8 @@ +package com.platinumrose + +import com.gildedrose.Item + +interface Solution { + fun items(): List + fun updateQuality() +} \ No newline at end of file diff --git a/Kotlin/src/main/kotlin/com/platinumrose/advance/AdvancePlatinumRose.kt b/Kotlin/src/main/kotlin/com/platinumrose/advance/AdvancePlatinumRose.kt new file mode 100644 index 00000000..f012d9d1 --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/advance/AdvancePlatinumRose.kt @@ -0,0 +1,23 @@ +package com.platinumrose.advance + +import com.gildedrose.Item +import com.platinumrose.ItemType.Companion.fromValue +import com.platinumrose.Solution +import com.platinumrose.advance.extention.updateQuality +import com.platinumrose.advance.factory.QualityCalculatorFactory +import com.platinumrose.advance.factory.QualityCalculatorFactoryImpl + +class AdvancePlatinumRose(private val items: List) : Solution { + + private val qualityCalculatorFactory: QualityCalculatorFactory = QualityCalculatorFactoryImpl() + + override fun items(): List { + return items + } + + override fun updateQuality() { + items.forEach { + it.updateQuality(qualityCalculatorFactory.qualityCalculator(fromValue(it.name))) + } + } +} diff --git a/Kotlin/src/main/kotlin/com/platinumrose/advance/extention/ItemExtention.kt b/Kotlin/src/main/kotlin/com/platinumrose/advance/extention/ItemExtention.kt new file mode 100644 index 00000000..79662eef --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/advance/extention/ItemExtention.kt @@ -0,0 +1,28 @@ +package com.platinumrose.advance.extention + +import com.gildedrose.Item +import com.platinumrose.advance.quality.QualityCalculator + + +fun Item.updateQuality(qualityCalculator: QualityCalculator) { + updateQualityBeforeSellInDate(qualityCalculator) + updateQualityAfterSellInDate(qualityCalculator) + coerceQuality(qualityCalculator) +} + +private fun Item.updateQualityBeforeSellInDate(qualityCalculator: QualityCalculator) { + val qualityIncrease = + qualityCalculator.computeQualityIncreaseBeforeSellIn(sellIn, quality) + sellIn += qualityCalculator.computeSellInDecrease(sellIn) + quality += qualityIncrease +} + +private fun Item.updateQualityAfterSellInDate(qualityCalculator: QualityCalculator) { + val qualityIncrease = + if (sellIn >= 0) 0 else qualityCalculator.computeQualityIncreaseAfterSellIn(sellIn, quality) + quality += qualityIncrease +} + +private fun Item.coerceQuality(qualityCalculator: QualityCalculator) { + quality = quality.coerceIn(com.platinumrose.ItemConstant.MIN_QUALITY, qualityCalculator.maxQuality()) +} \ No newline at end of file diff --git a/Kotlin/src/main/kotlin/com/platinumrose/advance/factory/QualityCalculatorFactory.kt b/Kotlin/src/main/kotlin/com/platinumrose/advance/factory/QualityCalculatorFactory.kt new file mode 100644 index 00000000..5607a27f --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/advance/factory/QualityCalculatorFactory.kt @@ -0,0 +1,9 @@ +package com.platinumrose.advance.factory + +import com.platinumrose.ItemType +import com.platinumrose.advance.quality.QualityCalculator + + +interface QualityCalculatorFactory { + fun qualityCalculator(itemType: ItemType): QualityCalculator +} \ No newline at end of file diff --git a/Kotlin/src/main/kotlin/com/platinumrose/advance/factory/QualityCalculatorFactoryImpl.kt b/Kotlin/src/main/kotlin/com/platinumrose/advance/factory/QualityCalculatorFactoryImpl.kt new file mode 100644 index 00000000..06e06068 --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/advance/factory/QualityCalculatorFactoryImpl.kt @@ -0,0 +1,22 @@ +package com.platinumrose.advance.factory + +import com.platinumrose.ItemType +import com.platinumrose.advance.quality.* + + +class QualityCalculatorFactoryImpl : QualityCalculatorFactory { + private val strategies: Map + private val regularItem = RegularItem() + + init { + val agedBrieUpdated = AgedBrie() + val backstagePasses = BackstagePasses() + val sulfuras = Sulfuras() + val strategiesList = listOf(agedBrieUpdated, backstagePasses, sulfuras) + strategies = strategiesList.associateBy { it.type() } + } + + override fun qualityCalculator(itemType: ItemType): QualityCalculator { + return strategies.getOrDefault(itemType, regularItem) + } +} \ No newline at end of file diff --git a/Kotlin/src/main/kotlin/com/platinumrose/advance/quality/AgedBrie.kt b/Kotlin/src/main/kotlin/com/platinumrose/advance/quality/AgedBrie.kt new file mode 100644 index 00000000..c3b4dca5 --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/advance/quality/AgedBrie.kt @@ -0,0 +1,28 @@ +package com.platinumrose.advance.quality + +import com.platinumrose.ItemConstant.Companion.REGULAR_ITEM_MAX_QUALITY +import com.platinumrose.ItemType.AGED_BRIE + + +internal class AgedBrie : QualityCalculator { + + override fun type(): com.platinumrose.ItemType { + return AGED_BRIE + } + + override fun maxQuality(): Int { + return REGULAR_ITEM_MAX_QUALITY + } + + override fun computeSellInDecrease(sellIn: Int): Int { + return -1 + } + + override fun computeQualityIncreaseBeforeSellIn(sellIn: Int, quality: Int): Int { + return 1 + } + + override fun computeQualityIncreaseAfterSellIn(sellIn: Int, quality: Int): Int { + return 1 + } +} \ No newline at end of file diff --git a/Kotlin/src/main/kotlin/com/platinumrose/advance/quality/BackstagePasses.kt b/Kotlin/src/main/kotlin/com/platinumrose/advance/quality/BackstagePasses.kt new file mode 100644 index 00000000..7fcf37ac --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/advance/quality/BackstagePasses.kt @@ -0,0 +1,32 @@ +package com.platinumrose.advance.quality + +import com.platinumrose.ItemConstant.Companion.REGULAR_ITEM_MAX_QUALITY +import com.platinumrose.ItemType.BACKSTAGE_PASSES + + +internal class BackstagePasses : QualityCalculator { + + override fun type(): com.platinumrose.ItemType { + return BACKSTAGE_PASSES + } + + override fun maxQuality(): Int { + return REGULAR_ITEM_MAX_QUALITY + } + + override fun computeSellInDecrease(sellIn: Int): Int { + return -1 + } + + override fun computeQualityIncreaseBeforeSellIn(sellIn: Int, quality: Int): Int { + return when (sellIn) { + in 0..5 -> 3 + in 6..10 -> 2 + else -> 1 + } + } + + override fun computeQualityIncreaseAfterSellIn(sellIn: Int, quality: Int): Int { + return -quality + } +} \ No newline at end of file diff --git a/Kotlin/src/main/kotlin/com/platinumrose/advance/quality/QualityCalculator.kt b/Kotlin/src/main/kotlin/com/platinumrose/advance/quality/QualityCalculator.kt new file mode 100644 index 00000000..32a3d367 --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/advance/quality/QualityCalculator.kt @@ -0,0 +1,11 @@ +package com.platinumrose.advance.quality + +interface QualityCalculator { + + fun type(): com.platinumrose.ItemType + fun maxQuality(): Int + + fun computeQualityIncreaseBeforeSellIn(sellIn: Int, quality: Int): Int; + fun computeSellInDecrease(sellIn: Int): Int + fun computeQualityIncreaseAfterSellIn(sellIn: Int, quality: Int): Int; +} \ No newline at end of file diff --git a/Kotlin/src/main/kotlin/com/platinumrose/advance/quality/RegularItem.kt b/Kotlin/src/main/kotlin/com/platinumrose/advance/quality/RegularItem.kt new file mode 100644 index 00000000..8ae1d5cb --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/advance/quality/RegularItem.kt @@ -0,0 +1,28 @@ +package com.platinumrose.advance.quality + +import com.platinumrose.ItemConstant.Companion.REGULAR_ITEM_MAX_QUALITY +import com.platinumrose.ItemType.REGULAR + + +internal class RegularItem : QualityCalculator { + + override fun type(): com.platinumrose.ItemType { + return REGULAR + } + + override fun maxQuality(): Int { + return REGULAR_ITEM_MAX_QUALITY + } + + override fun computeSellInDecrease(sellIn: Int): Int { + return -1 + } + + override fun computeQualityIncreaseBeforeSellIn(sellIn: Int, quality: Int): Int { + return -1 + } + + override fun computeQualityIncreaseAfterSellIn(sellIn: Int, quality: Int): Int { + return -1 + } +} \ No newline at end of file diff --git a/Kotlin/src/main/kotlin/com/platinumrose/advance/quality/Sulfuras.kt b/Kotlin/src/main/kotlin/com/platinumrose/advance/quality/Sulfuras.kt new file mode 100644 index 00000000..2ce55b1d --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/advance/quality/Sulfuras.kt @@ -0,0 +1,28 @@ +package com.platinumrose.advance.quality + +import com.platinumrose.ItemConstant.Companion.LEGENDARY_ITEM_MAX_QUALITY +import com.platinumrose.ItemType.SULFURAS + + +internal class Sulfuras : QualityCalculator { + + override fun type(): com.platinumrose.ItemType { + return SULFURAS + } + + override fun maxQuality(): Int { + return LEGENDARY_ITEM_MAX_QUALITY + } + + override fun computeSellInDecrease(sellIn: Int): Int { + return 0 + } + + override fun computeQualityIncreaseBeforeSellIn(sellIn: Int, quality: Int): Int { + return 0 + } + + override fun computeQualityIncreaseAfterSellIn(sellIn: Int, quality: Int): Int { + return 0 + } +} diff --git a/Kotlin/src/main/kotlin/com/platinumrose/mid/MidPlatinumRose.kt b/Kotlin/src/main/kotlin/com/platinumrose/mid/MidPlatinumRose.kt new file mode 100644 index 00000000..4e5d04b6 --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/mid/MidPlatinumRose.kt @@ -0,0 +1,36 @@ +package com.platinumrose.mid + +import com.gildedrose.Item +import com.platinumrose.ItemConstant.Companion.MIN_QUALITY +import com.platinumrose.Solution +import com.platinumrose.mid.strategy.* + + +class MidPlatinumRose(var items: List) : Solution { + private val strategies: Map + private val defaultStrategy = DefaultStrategy() + + init { + val agedBrieStrategy = AgedBrieStrategy() + val backstagePassesStrategy = BackstagePassesStrategy() + val sulfurasStrategy = SulfurasStrategy() + val strategiesList = listOf(agedBrieStrategy, backstagePassesStrategy, sulfurasStrategy) + strategies = strategiesList.associateBy { it.type() } + } + + override fun items(): List { + return items + } + + override fun updateQuality() { + for (item in items) { + val strategy = findUpdateQualityStrategy(item) + strategy.updateQuality(item) + item.quality = item.quality.coerceIn(MIN_QUALITY, strategy.maxQuality()) + } + } + + private fun findUpdateQualityStrategy(item: Item): UpdateQualityStrategy { + return strategies.getOrDefault(com.platinumrose.ItemType.fromValue(item.name), defaultStrategy) + } +} \ No newline at end of file diff --git a/Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/AgedBrieStrategy.kt b/Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/AgedBrieStrategy.kt new file mode 100644 index 00000000..f309842e --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/AgedBrieStrategy.kt @@ -0,0 +1,25 @@ +package com.platinumrose.mid.strategy + +import com.gildedrose.Item +import com.platinumrose.ItemConstant.Companion.REGULAR_ITEM_MAX_QUALITY +import com.platinumrose.ItemType.AGED_BRIE + + +internal class AgedBrieStrategy : UpdateQualityStrategy { + + override fun type(): com.platinumrose.ItemType { + return AGED_BRIE + } + + override fun updateQuality(item: Item) { + item.quality += 1 + item.sellIn -= 1 + if (item.sellIn < 0) { + item.quality += 1 + } + } + + override fun maxQuality(): Int { + return REGULAR_ITEM_MAX_QUALITY + } +} \ No newline at end of file diff --git a/Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/BackstagePassesStrategy.kt b/Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/BackstagePassesStrategy.kt new file mode 100644 index 00000000..41cb418b --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/BackstagePassesStrategy.kt @@ -0,0 +1,31 @@ +package com.platinumrose.mid.strategy + +import com.gildedrose.Item +import com.platinumrose.ItemConstant.Companion.REGULAR_ITEM_MAX_QUALITY +import com.platinumrose.ItemType.BACKSTAGE_PASSES + + +internal class BackstagePassesStrategy : UpdateQualityStrategy { + + override fun type(): com.platinumrose.ItemType { + return BACKSTAGE_PASSES + } + + override fun updateQuality(item: Item) { + item.quality += 1 + if (item.sellIn < 11) { + item.quality += 1 + } + if (item.sellIn < 6) { + item.quality += 1 + } + item.sellIn -= 1 + if (item.sellIn < 0) { + item.quality = 0 + } + } + + override fun maxQuality(): Int { + return REGULAR_ITEM_MAX_QUALITY + } +} \ No newline at end of file diff --git a/Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/DefaultStrategy.kt b/Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/DefaultStrategy.kt new file mode 100644 index 00000000..ae723516 --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/DefaultStrategy.kt @@ -0,0 +1,25 @@ +package com.platinumrose.mid.strategy + +import com.gildedrose.Item +import com.platinumrose.ItemConstant.Companion.REGULAR_ITEM_MAX_QUALITY +import com.platinumrose.ItemType.REGULAR + + +internal class DefaultStrategy : UpdateQualityStrategy { + + override fun type(): com.platinumrose.ItemType { + return REGULAR + } + + override fun updateQuality(item: Item) { + item.quality -= 1 + item.sellIn -= 1 + if (item.sellIn < 0) { + item.quality -= 1 + } + } + + override fun maxQuality(): Int { + return REGULAR_ITEM_MAX_QUALITY + } +} \ No newline at end of file diff --git a/Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/SulfurasStrategy.kt b/Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/SulfurasStrategy.kt new file mode 100644 index 00000000..0e20e2ce --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/SulfurasStrategy.kt @@ -0,0 +1,23 @@ +package com.platinumrose.mid.strategy + +import com.gildedrose.Item +import com.platinumrose.ItemConstant.Companion.LEGENDARY_ITEM_MAX_QUALITY +import com.platinumrose.ItemType.SULFURAS + + +internal class SulfurasStrategy : UpdateQualityStrategy { + + override fun type(): com.platinumrose.ItemType { + return SULFURAS + } + + override fun updateQuality(item: Item) { + // nothing to do here + } + + override fun maxQuality(): Int { + return LEGENDARY_ITEM_MAX_QUALITY + } +} + + diff --git a/Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/UpdateQualityStrategy.kt b/Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/UpdateQualityStrategy.kt new file mode 100644 index 00000000..e0af7073 --- /dev/null +++ b/Kotlin/src/main/kotlin/com/platinumrose/mid/strategy/UpdateQualityStrategy.kt @@ -0,0 +1,9 @@ +package com.platinumrose.mid.strategy + +import com.gildedrose.Item + +interface UpdateQualityStrategy { + fun type(): com.platinumrose.ItemType + fun updateQuality(item: Item) + fun maxQuality(): Int +} \ No newline at end of file diff --git a/Kotlin/src/main/kotlin/com/gildedrose/PlatinumRose.kt b/Kotlin/src/main/kotlin/com/platinumrose/simple/SimplePlatinumRose.kt similarity index 56% rename from Kotlin/src/main/kotlin/com/gildedrose/PlatinumRose.kt rename to Kotlin/src/main/kotlin/com/platinumrose/simple/SimplePlatinumRose.kt index a8e8cd81..e1bddb6d 100644 --- a/Kotlin/src/main/kotlin/com/gildedrose/PlatinumRose.kt +++ b/Kotlin/src/main/kotlin/com/platinumrose/simple/SimplePlatinumRose.kt @@ -1,6 +1,9 @@ -package com.gildedrose +package com.platinumrose.simple -class PlatinumRose(var items: List) { +import com.gildedrose.Item +import com.platinumrose.Solution + +class SimplePlatinumRose(var items: List) : Solution { companion object { const val MIN_QUALITY = 0 @@ -12,15 +15,20 @@ class PlatinumRose(var items: List) { const val LULFURAS_HAND_OF_RAGNAROK = "Sulfuras, Hand of Ragnaros" } - fun updateQuality() { + + override fun items(): List { + return items + } + + + override fun updateQuality() { for (item in items) { when (item.name) { - AGED_BRIE -> updateQualityForAgedBrie(item) - BACKSTAGE_PASSES -> updateQualityForBackstagePasses(item) - LULFURAS_HAND_OF_RAGNAROK -> null + com.platinumrose.ItemType.AGED_BRIE.value -> updateQualityForAgedBrie(item) + com.platinumrose.ItemType.BACKSTAGE_PASSES.value -> updateQualityForBackstagePasses(item) + com.platinumrose.ItemType.SULFURAS.value -> updateQualityForSulfuras(item) else -> updateQuality(item) } - item.quality = item.quality.coerceIn(0, REGULAR_ITEM_MAX_QUALITY) } } @@ -30,6 +38,7 @@ class PlatinumRose(var items: List) { if (item.sellIn < 0) { item.quality -= 1 } + item.quality = item.quality.coerceIn(0, REGULAR_ITEM_MAX_QUALITY) } private fun updateQualityForAgedBrie(item: Item) { @@ -38,6 +47,7 @@ class PlatinumRose(var items: List) { if (item.sellIn < 0) { item.quality += 1 } + item.quality = item.quality.coerceIn(0, REGULAR_ITEM_MAX_QUALITY) } private fun updateQualityForBackstagePasses(item: Item) { @@ -52,5 +62,10 @@ class PlatinumRose(var items: List) { if (item.sellIn < 0) { item.quality = 0 } + item.quality = item.quality.coerceIn(0, REGULAR_ITEM_MAX_QUALITY) + } + + private fun updateQualityForSulfuras(item: Item) { + item.quality = item.quality.coerceIn(0, LEGENDARY_ITEM_MAX_QUALITY) } } \ No newline at end of file diff --git a/Kotlin/src/test/kotlin/com/gildedrose/PlatinumRoseTest.kt b/Kotlin/src/test/kotlin/com/gildedrose/PlatinumRoseTest.kt deleted file mode 100644 index 2d8bea4f..00000000 --- a/Kotlin/src/test/kotlin/com/gildedrose/PlatinumRoseTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.gildedrose - -import com.gildedrose.PlatinumRose.Companion.AGED_BRIE -import com.gildedrose.PlatinumRose.Companion.BACKSTAGE_PASSES -import com.gildedrose.PlatinumRose.Companion.LULFURAS_HAND_OF_RAGNAROK -import com.gildedrose.PlatinumRose.Companion.MIN_QUALITY -import com.gildedrose.PlatinumRose.Companion.REGULAR_ITEM_MAX_QUALITY -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import java.util.* - -class PlatinumRoseTest { - - @Test - fun `should update quality for generated test cases and compare with GlidedRose`() { - val names = listOf( - AGED_BRIE, - BACKSTAGE_PASSES, - LULFURAS_HAND_OF_RAGNAROK, - "new none-existing on code name" - ) - val sellInRange = -100..100 - val qualityRange = MIN_QUALITY..REGULAR_ITEM_MAX_QUALITY - val allTestCases = generateTestCasesInRanger(names, sellInRange, qualityRange) - - - for (testCase in allTestCases) { - val platinumRose = PlatinumRose(listOf(Item(testCase.name, testCase.sellIn, testCase.quality))) - val gildedRose = GildedRose(listOf(Item(testCase.name, testCase.sellIn, testCase.quality))) - - platinumRose.updateQuality() - gildedRose.updateQuality() - - assertEquals(gildedRose.items, platinumRose.items) - } - } - - private fun generateTestCasesInRanger( - names: List, - sellInRange: IntRange, - qualityRange: IntRange - ): List { - val items = LinkedList() - for (name in names) { - for (sellIn in sellInRange) { - for (quality in qualityRange) { - items.add(Item(name, sellIn, quality)) - } - } - } - return items - } -} \ No newline at end of file diff --git a/Kotlin/src/test/kotlin/com/platinumrose/PlatinumRoseTestTemplate.kt b/Kotlin/src/test/kotlin/com/platinumrose/PlatinumRoseTestTemplate.kt new file mode 100644 index 00000000..e248623b --- /dev/null +++ b/Kotlin/src/test/kotlin/com/platinumrose/PlatinumRoseTestTemplate.kt @@ -0,0 +1,46 @@ +package com.platinumrose + +import com.gildedrose.GildedRose +import com.gildedrose.Item +import com.platinumrose.ItemConstant.Companion.MIN_QUALITY +import com.platinumrose.ItemConstant.Companion.REGULAR_ITEM_MAX_QUALITY +import org.junit.jupiter.api.Assertions.assertEquals +import java.util.* + +abstract class PlatinumRoseTestTemplate { + private val ITEMS_NAMES = listOf( + com.platinumrose.ItemType.AGED_BRIE.value!!, + com.platinumrose.ItemType.BACKSTAGE_PASSES.value!!, + com.platinumrose.ItemType.SULFURAS.value!!, + "new none-existing on code name" + ) + private val SELLIN_RANGE = -100..100 + private val QUALITY_RANGE = MIN_QUALITY..REGULAR_ITEM_MAX_QUALITY + + + fun generateTestCases(): List { + return generateTestCasesInRanger(ITEMS_NAMES, SELLIN_RANGE, QUALITY_RANGE) + } + + fun updateQualityAndCheckResult(gildedRose: GildedRose, simplePlatinumRose: Solution) { + simplePlatinumRose.updateQuality() + gildedRose.updateQuality() + assertEquals(gildedRose.items, simplePlatinumRose.items()) + } + + private fun generateTestCasesInRanger( + names: List, + sellInRange: IntRange, + qualityRange: IntRange + ): List { + val items = LinkedList() + for (name in names) { + for (sellIn in sellInRange) { + for (quality in qualityRange) { + items.add(Item(name, sellIn, quality)) + } + } + } + return items + } +} \ No newline at end of file diff --git a/Kotlin/src/test/kotlin/com/platinumrose/advance/AdvancePlatinumRoseTest.kt b/Kotlin/src/test/kotlin/com/platinumrose/advance/AdvancePlatinumRoseTest.kt new file mode 100644 index 00000000..24e8497f --- /dev/null +++ b/Kotlin/src/test/kotlin/com/platinumrose/advance/AdvancePlatinumRoseTest.kt @@ -0,0 +1,17 @@ +package com.platinumrose.advance + +import com.gildedrose.GildedRose +import com.gildedrose.Item +import com.platinumrose.PlatinumRoseTestTemplate +import org.junit.jupiter.api.Test + +class AdvancePlatinumRoseTest : PlatinumRoseTestTemplate() { + + @Test + fun `should update quality for generated test cases and compare with GlidedRose`() { + val allTestCases = generateTestCases() + val gildedRose = GildedRose(allTestCases.map { Item(it.name, it.sellIn, it.quality) }) + val simplePlatinumRose = AdvancePlatinumRose(allTestCases.map { Item(it.name, it.sellIn, it.quality) }) + updateQualityAndCheckResult(gildedRose, simplePlatinumRose) + } +} \ No newline at end of file diff --git a/Kotlin/src/test/kotlin/com/platinumrose/advance/factory/QualityCalculatorFactoryImplTest.kt b/Kotlin/src/test/kotlin/com/platinumrose/advance/factory/QualityCalculatorFactoryImplTest.kt new file mode 100644 index 00000000..e4a0435f --- /dev/null +++ b/Kotlin/src/test/kotlin/com/platinumrose/advance/factory/QualityCalculatorFactoryImplTest.kt @@ -0,0 +1,38 @@ +package com.platinumrose.advance.factory + +import com.platinumrose.ItemType +import com.platinumrose.advance.quality.AgedBrie +import com.platinumrose.advance.quality.BackstagePasses +import com.platinumrose.advance.quality.RegularItem +import com.platinumrose.advance.quality.Sulfuras +import org.junit.jupiter.api.Assertions.assertInstanceOf +import org.junit.jupiter.api.Test + +class QualityCalculatorFactoryImplTest { + + private val qualityCalculatorFactory = QualityCalculatorFactoryImpl() + + @Test + fun `should return expected calculator for AGED_BRIE`() { + val qualityCalculator = qualityCalculatorFactory.qualityCalculator(ItemType.AGED_BRIE) + assertInstanceOf(AgedBrie::class.java, qualityCalculator) + } + + @Test + fun `should return expected calculator for BACKSTAGE_PASSES`() { + val qualityCalculator = qualityCalculatorFactory.qualityCalculator(ItemType.BACKSTAGE_PASSES) + assertInstanceOf(BackstagePasses::class.java, qualityCalculator) + } + + @Test + fun `should return expected calculator for SULFURAS`() { + val qualityCalculator = qualityCalculatorFactory.qualityCalculator(ItemType.SULFURAS) + assertInstanceOf(Sulfuras::class.java, qualityCalculator) + } + + @Test + fun `should return expected calculator for any other`() { + val qualityCalculator = qualityCalculatorFactory.qualityCalculator(ItemType.REGULAR) + assertInstanceOf(RegularItem::class.java, qualityCalculator) + } +} \ No newline at end of file diff --git a/Kotlin/src/test/kotlin/com/platinumrose/advance/quality/AgedBrieTest.kt b/Kotlin/src/test/kotlin/com/platinumrose/advance/quality/AgedBrieTest.kt new file mode 100644 index 00000000..36457ce3 --- /dev/null +++ b/Kotlin/src/test/kotlin/com/platinumrose/advance/quality/AgedBrieTest.kt @@ -0,0 +1,38 @@ +package com.platinumrose.advance.quality + +import com.platinumrose.ItemConstant.Companion.REGULAR_ITEM_MAX_QUALITY +import com.platinumrose.ItemType +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import kotlin.random.Random + +class AgedBrieTest { + + private val Random = Random(Int.MAX_VALUE) + private val item = AgedBrie() + + @Test + fun `should return expected type`() { + assertEquals(item.type(), ItemType.AGED_BRIE) + } + + @Test + fun `should return expected max quality`() { + assertEquals(item.maxQuality(), REGULAR_ITEM_MAX_QUALITY) + } + + @Test + fun `should return -1 when calculate sell in decrease`() { + assertEquals(item.computeSellInDecrease(Random.nextInt()), -1) + } + + @Test + fun `should return 1 when calculate quality increase before sell in`() { + assertEquals(item.computeQualityIncreaseBeforeSellIn(Random.nextInt(), Random.nextInt()), 1) + } + + @Test + fun `should return 1 when calculate quality increase after sell in`() { + assertEquals(item.computeQualityIncreaseAfterSellIn(Random.nextInt(), Random.nextInt()), 1) + } +} \ No newline at end of file diff --git a/Kotlin/src/test/kotlin/com/platinumrose/advance/quality/BackstagePassesTest.kt b/Kotlin/src/test/kotlin/com/platinumrose/advance/quality/BackstagePassesTest.kt new file mode 100644 index 00000000..4a212f3e --- /dev/null +++ b/Kotlin/src/test/kotlin/com/platinumrose/advance/quality/BackstagePassesTest.kt @@ -0,0 +1,51 @@ +package com.platinumrose.advance.quality + +import com.platinumrose.ItemConstant.Companion.REGULAR_ITEM_MAX_QUALITY +import com.platinumrose.ItemType.BACKSTAGE_PASSES +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import kotlin.random.Random + +class BackstagePassesTest { + + private val Random = Random(0) + private val item = BackstagePasses() + + @Test + fun `should return expected type`() { + assertEquals(item.type(), BACKSTAGE_PASSES) + } + + @Test + fun `should return expected max quality`() { + assertEquals(item.maxQuality(), REGULAR_ITEM_MAX_QUALITY) + } + + @Test + fun `should return -1 when calculate sell in decrease`() { + assertEquals(item.computeSellInDecrease(Random.nextInt()), -1) + } + + @Test + fun `should return 1 when calculate quality increase before sell in and quality greater than 10`() { + assertEquals(item.computeQualityIncreaseBeforeSellIn(Random.nextInt(12, Int.MAX_VALUE), Random.nextInt()), 1) + } + + @Test + fun `should return 2 when calculate quality increase before sell in and quality between 6 and 10`() { + assertEquals(item.computeQualityIncreaseBeforeSellIn(6, Random.nextInt()), 2) + assertEquals(item.computeQualityIncreaseBeforeSellIn(10, Random.nextInt()), 2) + } + + @Test + fun `should return 3 when calculate quality increase before sell in and quality between 0 and 5`() { + assertEquals(item.computeQualityIncreaseBeforeSellIn(0, Random.nextInt()), 3) + assertEquals(item.computeQualityIncreaseBeforeSellIn(5, Random.nextInt()), 3) + } + + @Test + fun `should return negative quality when calculate quality increase after sell in`() { + val quality = Random.nextInt() + assertEquals(item.computeQualityIncreaseAfterSellIn(Random.nextInt(), quality), -quality) + } +} \ No newline at end of file diff --git a/Kotlin/src/test/kotlin/com/platinumrose/advance/quality/RegularItemTest.kt b/Kotlin/src/test/kotlin/com/platinumrose/advance/quality/RegularItemTest.kt new file mode 100644 index 00000000..cf567075 --- /dev/null +++ b/Kotlin/src/test/kotlin/com/platinumrose/advance/quality/RegularItemTest.kt @@ -0,0 +1,38 @@ +package com.platinumrose.advance.quality + +import com.platinumrose.ItemConstant +import com.platinumrose.ItemType +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import kotlin.random.Random + +class RegularItemTest { + + private val Random = Random(Int.MAX_VALUE) + private val item = RegularItem() + + @Test + fun `should return expected type`() { + assertEquals(item.type(), ItemType.REGULAR) + } + + @Test + fun `should return expected max quality`() { + assertEquals(item.maxQuality(), ItemConstant.REGULAR_ITEM_MAX_QUALITY) + } + + @Test + fun `should return -1 when calculate sell in decrease`() { + assertEquals(item.computeSellInDecrease(Random.nextInt()), -1) + } + + @Test + fun `should return -1 when calculate quality increase before sell in`() { + assertEquals(item.computeQualityIncreaseBeforeSellIn(Random.nextInt(), Random.nextInt()), -1) + } + + @Test + fun `should return -1 when calculate quality increase after sell in`() { + assertEquals(item.computeQualityIncreaseAfterSellIn(Random.nextInt(), Random.nextInt()), -1) + } +} \ No newline at end of file diff --git a/Kotlin/src/test/kotlin/com/platinumrose/advance/quality/SulfurasTest.kt b/Kotlin/src/test/kotlin/com/platinumrose/advance/quality/SulfurasTest.kt new file mode 100644 index 00000000..afe3ed77 --- /dev/null +++ b/Kotlin/src/test/kotlin/com/platinumrose/advance/quality/SulfurasTest.kt @@ -0,0 +1,38 @@ +package com.platinumrose.advance.quality + +import com.platinumrose.ItemConstant +import com.platinumrose.ItemType +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import kotlin.random.Random + +class SulfurasTest { + + private val Random = Random(Int.MAX_VALUE) + private val item = Sulfuras() + + @Test + fun `should return expected type`() { + assertEquals(item.type(), ItemType.SULFURAS) + } + + @Test + fun `should return expected max quality`() { + assertEquals(item.maxQuality(), ItemConstant.LEGENDARY_ITEM_MAX_QUALITY) + } + + @Test + fun `should return 0 when calculate sell in decrease`() { + assertEquals(item.computeSellInDecrease(Random.nextInt()), 0) + } + + @Test + fun `should return 0 when calculate quality increase before sell in`() { + assertEquals(item.computeQualityIncreaseBeforeSellIn(Random.nextInt(), Random.nextInt()), 0) + } + + @Test + fun `should return 0 when calculate quality increase after sell in`() { + assertEquals(item.computeQualityIncreaseAfterSellIn(Random.nextInt(), Random.nextInt()), 0) + } +} \ No newline at end of file diff --git a/Kotlin/src/test/kotlin/com/platinumrose/mid/MidPlatinumRoseTest.kt b/Kotlin/src/test/kotlin/com/platinumrose/mid/MidPlatinumRoseTest.kt new file mode 100644 index 00000000..8cb9ab17 --- /dev/null +++ b/Kotlin/src/test/kotlin/com/platinumrose/mid/MidPlatinumRoseTest.kt @@ -0,0 +1,18 @@ +package com.platinumrose.mid + +import com.gildedrose.GildedRose +import com.gildedrose.Item +import com.platinumrose.PlatinumRoseTestTemplate +import org.junit.jupiter.api.Test + + +class MidPlatinumRoseTest : PlatinumRoseTestTemplate() { + + @Test + fun `should update quality for generated test cases and compare with GlidedRose`() { + val allTestCases = generateTestCases() + val gildedRose = GildedRose(allTestCases.map { Item(it.name, it.sellIn, it.quality) }) + val solution = MidPlatinumRose(allTestCases.map { Item(it.name, it.sellIn, it.quality) }) + updateQualityAndCheckResult(gildedRose, solution) + } +} \ No newline at end of file diff --git a/Kotlin/src/test/kotlin/com/platinumrose/simple/SimplePlatinumRoseTest.kt b/Kotlin/src/test/kotlin/com/platinumrose/simple/SimplePlatinumRoseTest.kt new file mode 100644 index 00000000..f5acf1e9 --- /dev/null +++ b/Kotlin/src/test/kotlin/com/platinumrose/simple/SimplePlatinumRoseTest.kt @@ -0,0 +1,17 @@ +package com.platinumrose.simple + +import com.gildedrose.GildedRose +import com.gildedrose.Item +import com.platinumrose.PlatinumRoseTestTemplate +import org.junit.jupiter.api.Test + +class SimplePlatinumRoseTest : PlatinumRoseTestTemplate() { + + @Test + fun `should update quality for generated test cases and compare with GlidedRose`() { + val allTestCases = generateTestCases() + val gildedRose = GildedRose(allTestCases.map { Item(it.name, it.sellIn, it.quality) }) + val solution = SimplePlatinumRose(allTestCases.map { Item(it.name, it.sellIn, it.quality) }) + updateQualityAndCheckResult(gildedRose, solution) + } +} \ No newline at end of file