From c58105b8ca2ab9b1ece5d6023f759f17f80d4770 Mon Sep 17 00:00:00 2001 From: z <22ad0414@gmail.com> Date: Wed, 14 May 2025 16:01:58 +0900 Subject: [PATCH] =?UTF-8?q?test=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/co/jp/app/UserServiceTest.java | 168 ++++++++++++++++++ src/test/java/co/jp/app/UserServiceTest1.java | 70 ++++++++ .../co/jp/app/UserServiceTest$1.class | Bin 0 -> 1004 bytes .../co/jp/app/UserServiceTest$2.class | Bin 0 -> 1002 bytes .../co/jp/app/UserServiceTest$3.class | Bin 0 -> 1000 bytes .../co/jp/app/UserServiceTest$4.class | Bin 0 -> 979 bytes .../co/jp/app/UserServiceTest.class | Bin 0 -> 7089 bytes .../co/jp/app/UserServiceTest1.class | Bin 0 -> 3018 bytes 8 files changed, 238 insertions(+) create mode 100644 src/test/java/co/jp/app/UserServiceTest.java create mode 100644 src/test/java/co/jp/app/UserServiceTest1.java create mode 100644 target/test-classes/co/jp/app/UserServiceTest$1.class create mode 100644 target/test-classes/co/jp/app/UserServiceTest$2.class create mode 100644 target/test-classes/co/jp/app/UserServiceTest$3.class create mode 100644 target/test-classes/co/jp/app/UserServiceTest$4.class create mode 100644 target/test-classes/co/jp/app/UserServiceTest.class create mode 100644 target/test-classes/co/jp/app/UserServiceTest1.class diff --git a/src/test/java/co/jp/app/UserServiceTest.java b/src/test/java/co/jp/app/UserServiceTest.java new file mode 100644 index 0000000..7a41e82 --- /dev/null +++ b/src/test/java/co/jp/app/UserServiceTest.java @@ -0,0 +1,168 @@ +package co.jp.app; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.function.Executable; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.PasswordEncoder; + +import co.jp.app.common.ResultCode; +import co.jp.app.dto.RegistrationDto; +import co.jp.app.entity.UserEntity; +import co.jp.app.exception.BusinessException; +import co.jp.app.repository.UserRepository; +import co.jp.app.service.UserService; + +@ExtendWith(MockitoExtension.class) +public class UserServiceTest { + + @Mock + private UserRepository userRepository; + + @Mock + private PasswordEncoder passwordEncoder; + + @InjectMocks + private UserService userService; + + private RegistrationDto registrationDto; + private UserEntity userEntity; + + @BeforeEach + public void setUp() { + registrationDto = new RegistrationDto(); + registrationDto.setName("Test User"); + registrationDto.setEmail("test@example.com"); + registrationDto.setPassword("password123"); + + userEntity = new UserEntity(); + userEntity.setName("Test User"); + userEntity.setEmail("test@example.com"); + userEntity.setPassword("encodedPassword"); + } + + @Test + @DisplayName("新用户注册成功") + public void registerNewUser_success() throws BusinessException { + // Arrange + when(userRepository.existsByEmail(anyString())).thenReturn(false); + when(passwordEncoder.encode(anyString())).thenReturn("encodedPassword"); + when(userRepository.save(any(UserEntity.class))).thenReturn(userEntity); + + // Act + UserEntity savedUser = userService.registerNewUser(registrationDto); + + // Assert + assertNotNull(savedUser); + assertEquals(userEntity.getEmail(), savedUser.getEmail()); + assertEquals("encodedPassword", savedUser.getPassword()); + verify(userRepository, times(1)).existsByEmail("test@example.com"); + verify(passwordEncoder, times(1)).encode("password123"); + verify(userRepository, times(1)).save(any(UserEntity.class)); + } + + @Test + @DisplayName("用户注册 - 邮箱已存在") + public void registerNewUser_emailAlreadyExists() { + // Arrange + when(userRepository.existsByEmail(anyString())).thenReturn(true); + + // Act & Assert + BusinessException exception = assertThrows(BusinessException.class, new Executable() { + public void execute() throws Throwable { + userService.registerNewUser(registrationDto); + } + }); + + assertEquals(ResultCode.USER_EMAIL_ALREADY_EXISTS, exception.getResultCode()); + verify(userRepository, times(1)).existsByEmail("test@example.com"); + verify(passwordEncoder, never()).encode(anyString()); + verify(userRepository, never()).save(any(UserEntity.class)); + } + + @Test + @DisplayName("用户注册 - 密码过短") + public void registerNewUser_passwordTooShort() { + // Arrange + registrationDto.setPassword("123"); // 设置一个短密码 + when(userRepository.existsByEmail(anyString())).thenReturn(false); + + // Act & Assert + BusinessException exception = assertThrows(BusinessException.class, new Executable() { + public void execute() throws Throwable { + userService.registerNewUser(registrationDto); + } + }); + + assertEquals(ResultCode.USER_PASSWORD_TOO_SHORT, exception.getResultCode()); + verify(userRepository, times(1)).existsByEmail("test@example.com"); + verify(passwordEncoder, never()).encode(anyString()); + verify(userRepository, never()).save(any(UserEntity.class)); + } + + @Test + @DisplayName("用户注册 - 密码为null") + public void registerNewUser_passwordIsNull() { + // Arrange + registrationDto.setPassword(null); // 设置密码为null + when(userRepository.existsByEmail(anyString())).thenReturn(false); + + // Act & Assert + BusinessException exception = assertThrows(BusinessException.class, new Executable() { + public void execute() throws Throwable { + userService.registerNewUser(registrationDto); + } + }); + + assertEquals(ResultCode.USER_PASSWORD_TOO_SHORT, exception.getResultCode()); + verify(userRepository, times(1)).existsByEmail("test@example.com"); + verify(passwordEncoder, never()).encode(anyString()); + verify(userRepository, never()).save(any(UserEntity.class)); + } + + @Test + @DisplayName("通过邮箱加载用户 - 用户存在") + public void loadUserByUsername_userFound() { + // Arrange + when(userRepository.findByEmail(anyString())).thenReturn(Optional.of(userEntity)); + + // Act + UserDetails userDetails = userService.loadUserByUsername("test@example.com"); + + // Assert + assertNotNull(userDetails); + assertEquals(userEntity.getEmail(), userDetails.getUsername()); + assertEquals(userEntity.getPassword(), userDetails.getPassword()); + assertTrue(userDetails.getAuthorities().stream() + .anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals("ROLE_USER"))); + verify(userRepository, times(1)).findByEmail("test@example.com"); + } + + @Test + @DisplayName("通过邮箱加载用户 - 用户不存在") + public void loadUserByUsername_userNotFound() { + // Arrange + when(userRepository.findByEmail(anyString())).thenReturn(Optional.empty()); + + // Act & Assert + UsernameNotFoundException exception = assertThrows(UsernameNotFoundException.class, new Executable() { + public void execute() throws Throwable { + userService.loadUserByUsername("unknown@example.com"); + } + }); + + assertEquals("unknown@example.com not found", exception.getMessage()); + verify(userRepository, times(1)).findByEmail("unknown@example.com"); + } +} \ No newline at end of file diff --git a/src/test/java/co/jp/app/UserServiceTest1.java b/src/test/java/co/jp/app/UserServiceTest1.java new file mode 100644 index 0000000..192cc47 --- /dev/null +++ b/src/test/java/co/jp/app/UserServiceTest1.java @@ -0,0 +1,70 @@ +package co.jp.app; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.crypto.password.PasswordEncoder; + +import co.jp.app.dto.RegistrationDto; +import co.jp.app.entity.UserEntity; +import co.jp.app.exception.BusinessException; +import co.jp.app.repository.UserRepository; +import co.jp.app.service.UserService; + +@ExtendWith(MockitoExtension.class) +public class UserServiceTest1 { + + @Mock + private UserRepository userRepository; + + @Mock + private PasswordEncoder passwordEncoder; + + @InjectMocks + private UserService userService; + + private RegistrationDto registrationDto; + private UserEntity userEntity; + + @BeforeEach + public void setUp() { + registrationDto = new RegistrationDto(); + registrationDto.setName("Test User"); + registrationDto.setEmail("test@example.com"); + registrationDto.setPassword("password123"); + + userEntity = new UserEntity(); + userEntity.setName("Test User"); + userEntity.setEmail("test@example.com"); + userEntity.setPassword("encodedPassword"); + } + + @Test + @DisplayName("新用户注册成功") + public void registerNewUser_success() throws BusinessException { + // Arrange + when(userRepository.existsByEmail(anyString())).thenReturn(false); + when(passwordEncoder.encode(anyString())).thenReturn("encodedPassword"); + when(userRepository.save(any(UserEntity.class))).thenReturn(userEntity); + + // Act + UserEntity savedUser = userService.registerNewUser(registrationDto); + + // Assert + assertNotNull(savedUser); + assertEquals(userEntity.getEmail(), savedUser.getEmail()); + assertEquals("encodedPassword", savedUser.getPassword()); + verify(userRepository, times(1)).existsByEmail("test@example.com"); + verify(passwordEncoder, times(1)).encode("password123"); + verify(userRepository, times(1)).save(any(UserEntity.class)); + } + +} diff --git a/target/test-classes/co/jp/app/UserServiceTest$1.class b/target/test-classes/co/jp/app/UserServiceTest$1.class new file mode 100644 index 0000000000000000000000000000000000000000..4b5ea02b1217f05611df88d8f96576cda68f471f GIT binary patch literal 1004 zcmaJ=TTc@~6#k|yEL~PvDPFL05v#UESx9`Vk`S!X1ge38k7l|Z+iAAD&FmD)n}5j! zHSxh8;Eyt%*%Dd|waJ;w*)!kwolAfJ`S}aLD{NUvG35Qo>-0R{>v>19P=`XDNMF=N ztP2#7VQ@SAgnI!GTi*LdNBG*pG(#y;Ew9rLrKYtfwNL~hz0dv7*D?yd>OlB?%^QJW znAU9>7dDA&Zz5TR!d^;(+h$lU{wJ<<%#hiQnj#B^8xAbY*hpi}K^87Ux@ZFC_GBn( z{cc03`cT9CUgY!Om@8@S7tV|k#;`P*uCPVI+=yelYEdSZzrKT6K74t&uw zImZ?j8FHg?)!QmMHFR0zv1H)^!{WpOI9SFC!)$-l1csF{W#l)V8ShfX06%oFhDVgM z5-k~P#f_HNI=cG0sUz>;YCy%tV?4F+geowSbq$S!B1)G8Le<2n85M@@@kU+8P#V)O zLM`=KBEOm(sj~KA)K|WEBh8pB{4;+WrjkTk4gDaBW!M_F!NG=U^x81pOEsTFm&;%$ zP=YtlsspM7waIxGhC=NIJdOojXKFOoZ=+aam4+uxHU;LoW`^h=ks`~&rhS%9FK9O& zyS)AlPWcCJeobO-(K<~pfdkCb>Lh0yIV{laHrW)7#}}8}cim9TehoROZ}+W78Mn!c5CAs+Qd|hCWuC`KAL5j!VtQ%nH@@f^DmiH zO?>bN_@j(>mZnfs++@D)+&SmmkNy4U=Pv-y@Yq6%Asow2+}!JLbVq`|SZ| znAbfK7q^M4K9j6UZchcl?J}&F{^M8PXUOcy4$p$&k^>71HquyfkcG>TE*XcVx(In= zc-ZEuHP*0Pmw`Xn_mwc`Q)9*mV_2I_SKJ|C?i;Rqvh&VY{voGG>50XY_$WBLb{z1? z#2i~#Wynp^)#@oZGIUwwv1Z{G!|KcgI9SI9!{TsK1cr?%Wn?#%nXXdR0N-|S2X`rE z#k(Tb$~RhGXnFp1M@#Shd55Zvdw5{sK4oAc>jD}FC6vzyxN7i`X%&Xu=|Ww^P@dAy zLoM{@M1C#VQfBR@9IAl76sAp9{^`FhlS!hjg~33^BJ7UK;9$!Xx-bs+OwFgrkK>V4 zotBi%o>ZE_c^!sa?G5OHQ_U6{)YPwJtWluWNz<4Eb6wLx^gpC%WMR{~NV_Mr8jD@o z{067;1DC%hK38a-rh5TOx68drg literal 0 HcmV?d00001 diff --git a/target/test-classes/co/jp/app/UserServiceTest$3.class b/target/test-classes/co/jp/app/UserServiceTest$3.class new file mode 100644 index 0000000000000000000000000000000000000000..b4462d6864c031dd6da93835807d3741158975a8 GIT binary patch literal 1000 zcmaJ=YflqF6g|_HE?rhxDL%0BP^;3SEC#=!BnE3FDb+~Dk7m0a+i8}aW_A|PXyRWo zK~4PN5Aa7B?`#PzhT7!Lhsu>7C6${s^*OSX9d3>O?&n6#0_w1Wa%hU~Hln68V6 zHn)Ty2Hq=G3vt295L!8EY)j;AF`rz+BhN5B(c5WChr?PtoZS?Z#tQ zSH8ii{=mhrY0M>B^Yjw&o1oG7;*$FcT!Oett{mO2rLa%QV;&c) z$8e5ueXuQkNBQj#-)I0n5&q{9j^YVN4psV^!3yrD&?P*8M<<(r4Yc{Kk!4W&3u!3% A!vFvP literal 0 HcmV?d00001 diff --git a/target/test-classes/co/jp/app/UserServiceTest$4.class b/target/test-classes/co/jp/app/UserServiceTest$4.class new file mode 100644 index 0000000000000000000000000000000000000000..ee505774dfb74d25652cc2ecf5525109be0cae0d GIT binary patch literal 979 zcmaJ`A>}g z0~6H52Y-M+%6O-cltgipnYs7w%sKbobASE*@e{xkL>>wZl~_mJG~#I*z0HJa3v(!A zu_ZEFTk=q32)q1{M+sMZ(dJH9#MZ+ILqnUrsGBQkX-}mUh9G3Lo2%GLt)jJK5$Bfg zB!XeY?#rz9n7CS(k}cD<6-jVU8RqK$x!2fcD6Z;`D1l)-fQL~ZLzoOuLdY;wcNZpG zQi+YcwzFn7y2?wnT}AuZ7*$oj2U@o}fzUi6{E?QE~p- zaV%0-bLL@&VQSE>t$m}999;<&%zC)RFmq`G0?eVxFqRKQV5nZmjN(q2iz+RT;x;kn z>Okou^-LV|UYdx-SoeI~#XS!-DsI5|4}<{s@qnS6Xx?%1KR#ad8ZEK|3 zTW++R?pf-bUDu8yZ4RQ0v^LV7M6os^ausxhk^ zZPAgDbZaV3bVm4RT@Uck>013e!}pG8L)&$os}4i(N-1Gh6P{&)6dK*25q_yNi+LK9 zAzH&=Vd&mOpD(~qoWmDUr@sX> W(CnjDuz*Ko`Go0E-M2`qfXW|!Gxl%* literal 0 HcmV?d00001 diff --git a/target/test-classes/co/jp/app/UserServiceTest.class b/target/test-classes/co/jp/app/UserServiceTest.class new file mode 100644 index 0000000000000000000000000000000000000000..5f37fd0823ea1a5925e2073b5dfc4b5e2dc4b5af GIT binary patch literal 7089 zcmcgw33wFc8GipHFkv$st_Y%l0RtvMSc#y8AR5R5u?fL!IJJ$F-AS^rnOS#cL!zx` z3zSN$6~$Ux?4`8z!Xs>;DsAnh_OO@sz67*)t-a6p|M!~BrR90r@US!UpWpi(@Av)x zGygjLB!G)Vm4+gLX<<9CH6AeH@xXe=OoYrtPc&?{nvScXL}1cZqsIuu468HH+`iQe zy8;uE9M@vTZ71s5i9UfjjrlbaxpuW&epoHHYDrpdwAldUjf1UEzE#Stf!&rN*!k+h8Q3@;lR5;&w%;;xj$F4Kg+N8snJm`nbSs1$b+c8wa)~ z<5Aa4&^e-k6=sK>FoQ<8t5(BI%AU9RFi3rvfhpr4FiXb-Of1FO0^_7pmCK!F?;IU- zagKn-eb>^E0;g3r=AA9%N)y*sZ74+*z0l>Cl>vlN&2A zeFR%bMbczvkI-Eob>cCjPij>fw#SGirFxIw@$iYg2afMLbo|i+$9CL&eAg|)b5tv+AHPm2bXhO4wwUjOoazs+lu@09B zj5m83N6v~qWyTRaVT%tTtk=*ga7x}!R@ipTG%Ot(u#wKBur_xHOsU-Bl`b=^R)XBD zV+$^4IBoATErF$8w@Y(eZB;?kJ8rVQo!%H|wmNM-Lz(Z|GQ0(C8r~{U;Z@m4<8@pq z_w~8lx5adm2}^3fUB@{1$`C<^2HI(vTbok*PLmMx zO3VbCRL)i%+Ylq;(9m?$g6T1`X~DdVwxrEz0UM-`636Hvh`HX0Ii==z%rr^-2_0q7 zeQ?pEAz45y!%(u0?dZ)>Z!XrD87e*WHVh+C@Vr9~33xij)p(nRYXr)r{)h5kokM9< z2NFOx0TTu|9i=+Hf8BH9rRtGpH1;Q44` zf>AN@9v$z+Ep#L`l%8?>lglRWH$Hgc!Gp&RJ#y@UJCEIcK*Q|?v_2@wRV?&jFZRjYc_-KM`pck^Ogqv< zcj@>5?#`L0wJTw7r#eGkRjgD&F#-{HB(1P?nLx0Y6^biUazXs46;A&=v{L-vlNay(o8E)=s9MJIze3G7JnY6dSe9n2}PC$#B*>p!w_w1Hy9-0N2 z!O92wMmEK_+IFbRPOv6DbC72oduYdrdv197shubGKTwK?$!9DFG(1wE-$7QdsPW+- z_DNGbuA^USdaBa&y1GzkV{=P=TWfQ3TWC#l3sca{kz{Ic(sVF=SczUr9k$388V-*t z2`iTY>|+5Cvkes>EBd6c7BhNVk}s?5q!pnGU%CD+V$6iW?t5N-;)(RGjE(e5Ixr+b zKc(X+K0~tTh+2_crmgh)a<=A5^G(u?#sbZ%YB6H;oX_F&8lGl}saU+ASH~CdMS&ul ziML?IEL{!1qKhx;m6FuvRY=?6b#RC(Yk~g zk=$=``=8?H8h%DU&u?F_td5s(jERfIK$GEySy{}=7)3!DX3Z*=X;TZ7%bn2-O3oVUH!41ulrtex{kkFYQff~+N~DYbR>$w~ zdlH=49hWDxDFr&rMT3Sv2(13^y+{UdR<>N7ui=lQlEES$Ucyh9R#53k~HI{q#_)JG?5;%UO@G)wVM*7U>76lgdJ?GKBAEL|Eiy4xd01y4d3 zR=$CCS0@b1H6z(b>8q{UQYMN-i6)8#YF=NrgXimFj0CQGqOY%1XiPdS&5gk}$>csU zPLyfFS8%Pt0AfwA3tfztg>$A38@8I;!ik-O#1>d|GGOUijkfow-WSgv*L@*7nFyP! zqVhmLS<*}>e<`y-Mk?!iwdc?()$Dam%So3Ue2Z+1x?QwQdU*DBrEr?O&ENqqn4QVt zlXzTmNd!i`$s~_O9G{pfrfK4I)@h0<)O}FY>an+(fktI^4m3K9u)Gk`#TnvE#(8(X zN#HpznP+CE7hhvok(lXJG}`vIWW3f>?4~W_Z7G=8N|l{lXL`%g=XxvF%(g3ySS-W~ zk=lu4#gs{6ybnU0rQ_c`j*JyECkY;A^ajf^6RIdQ9Zgj52sK1u!@<_XT-Fq$52bge zbwu5+CBrwF^5VzAEbd!O`X7TLd5-6o#xMCS<+DhhE{C-7#N}xlug3A7Mm>4q!KXZ$ z@sf$Zllc|kQ}8*a`VdY#sD@4DuiONfj??)o7nDLw!x?;%(Kxv%C(3lT$%Xb&R5`Ax z`UqxhK7_L#M|t&QDEH6qM}>N+>_@eFnct59N7ZoDqQ|kAZHwio3)Krpy-B?+=|^o| zpt+PAycR0Md~UaZ_dfw%GcDx58Z5>6Scyew#A1)YO*w&=QQ%@+#Q7x@ak&zGA|YJD zmO8eqa5$Y9L^&Ia*!VOBs83@zcxuIxdB)4tGpjhjAX!F^c6 zMA@KJI3=xzj0&&h*jwp5(pBD5BizP+|5MaLf6@Z3$d9+^A?10Ij+SO z9<87n!TI1X%^pX>_prHeOCth1s>>!MuMJF&|kMZ ztvt?M%?tqSrgQHh8{UFyhUrp<=%u)gc=w`>&vx97Ufkiq+?T;Tm-nu}z%P}f|7s-6 zzgC#{WUSpV2y+U*N#pgudIYaY3;#)3_|-=LpXu6v9U9U97(gM0DNHTE-IQ-Xs(6D@ zgL_Ft_j%-P$jDoTat7wVASU@_Zl>Ha@ifpJGVzSB9uR`{uo&AfO7CYwG4h#guUamh z$;OKbf(cPf;+TRKj1e+H774%Nj0vm)WSU%YIYd}Y7N@1hi|Hs9Geo(VC1$gwn&R)} jQ~pWLm45^22djJaC(agg#5tHID*5T7CiimW0ABkKauXS% literal 0 HcmV?d00001 diff --git a/target/test-classes/co/jp/app/UserServiceTest1.class b/target/test-classes/co/jp/app/UserServiceTest1.class new file mode 100644 index 0000000000000000000000000000000000000000..89c7ff9fa8ecd0dc40652e947b443f2b63b33204 GIT binary patch literal 3018 zcmbVO*>@9F82?S$nvxD%fFcM|_Oyi#6j_4>N&})@pe-$n3e(ALJ0+P3vvd*n1rbp} z5g&b&M?FeO^&Ai9=#&2k^}q1=yOT7T(AEbZl6&uWzxyq}Z=1jV`So`IFXKTJ5e4fF zTN}%1dM>A(cZJg@oN?0-{lfM3Mp3O`#h5;>YZ=`d(Rzl)gyAVz=995k6$|sal*1}PxnmQauvioZD&Nw+Qu~s)w;Oy!SbB$ zx)ZjOPFaSX7LJ0pfX&T0rZqC`=vl$rYnm$z-!Z)@&2Xl2d_MO~J6rjlBI~5_MK2X> z3RzCNbUh4%w7J5rGnRxvTIWjh95G_Lo)h@c;n|h)(ln&El7VkxQh$mtcm!@^NWBPX zf%d{RnV&mqTBg^gpeoTgprHCBL5QIW^(x|6MFTobOLY6$A>s7vL^OcW&@%(NW6E=p zSnZ9Pgm^Dx%Q~oE4k{(HJ1-j>{IYU z*@1cB$Ix0Z|A2}WSSgR&Y*`FUiy?JN$*4?MKzn3vesi?toF`UMkC|*&p>e*UkO{#bmuQ7(YHB|_W zg4Kz}`2nj#7kZ-Trgdc}7wrlv&f=VcrDBqWE#OSsH>niv(9f+OLyD>S)eV@27!o{=2362@O;OT=ntt_f><5W7>A$0*4r&Y@#-s2n8p{GbS;YhTy>354#46g?oXZ2UH@vUn16e$^ zBgIA~m3^e*V|=1OEr>~7_w@{0#m)tQ3q`6z?x!klNx5~DTkb`taa+abxWmeq)0Q$@ z5@#wCdR#c>uxSK?OB)Csx;SBi8u?PiSGdbW3Rsy5=N7Unob>-KGn{QjAJ|M}uB2tF zWKs0lzGH|}rW|8yo}EuEGIN;Ci!?kj=?Tl_mxPv*yY!&xjk5C<*ylbfD%+?W;%sXH zzkHd*X69lQKaFY-k?oMDC{J>&nVd%DKU{iuWAW{9nzJ##P~3 z)ASf?ehS#u@?RB~vz#CIx*L2nM>Ee#6F#kFn_y8k&AV zLww6LwgsESG@63V?rCV`Y9-f8kFcM#{gUfoup!r>U~_mH$+AH^=q<35g_)qbMvl=Y zPU~Hqq`PqhEjUR}I@X3!Zy9=&;7sNyn%*CQC@ogUm7^6o