├── .gitignore
├── HELP.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
├── java
│ └── idv
│ │ └── kuma
│ │ └── itehlp2021
│ │ ├── HelloController.java
│ │ ├── Itehlp2021Application.java
│ │ ├── course
│ │ ├── ClassRoom.java
│ │ ├── Course.java
│ │ ├── CourseRepository.java
│ │ ├── DistanceCalculator.java
│ │ ├── DistanceChecker.java
│ │ └── Position.java
│ │ ├── scholarship
│ │ ├── BachelorScholarshipCalculator.java
│ │ ├── Calculator.java
│ │ ├── Course.java
│ │ ├── MasterScholarshipCalculator.java
│ │ ├── PhDScholarshipCalculator.java
│ │ ├── ScholarshipService.java
│ │ ├── Transcript.java
│ │ ├── command
│ │ │ ├── adapter
│ │ │ │ ├── ApplicationForm.java
│ │ │ │ ├── ApplicationRepositoryImpl.java
│ │ │ │ ├── ApplyScholarshipController.java
│ │ │ │ ├── ClientSideErrorException.java
│ │ │ │ ├── ScholarshipRepositoryImpl.java
│ │ │ │ └── ServerSideErrorException.java
│ │ │ ├── entity
│ │ │ │ ├── Application.java
│ │ │ │ └── Scholarship.java
│ │ │ └── usecase
│ │ │ │ ├── ApplicationChecker.java
│ │ │ │ ├── ApplicationRepository.java
│ │ │ │ ├── ApplyScholarshipService.java
│ │ │ │ ├── ApplyScholarshipServiceCodeFirst.java
│ │ │ │ ├── RepositoryAccessDataFailException.java
│ │ │ │ └── ScholarshipRepository.java
│ │ ├── find_and_notify
│ │ │ ├── FindTopAndNotifyService.java
│ │ │ └── TranscriptRepository.java
│ │ └── notify
│ │ │ ├── Mailer.java
│ │ │ ├── ScholarshipResult.java
│ │ │ └── SendResultEmailService.java
│ │ └── student
│ │ ├── Student.java
│ │ └── register
│ │ ├── ApiResponse.java
│ │ ├── DataNotFoundException.java
│ │ ├── RegisterController.java
│ │ ├── RegisterRequest.java
│ │ ├── RegisterService.java
│ │ ├── StudentNotExistException.java
│ │ ├── StudentRepository.java
│ │ └── StudentRepositoryImpl.java
└── resources
│ └── application.properties
└── test
└── java
└── idv
└── kuma
└── itehlp2021
├── Itehlp2021ApplicationTests.java
├── course
└── DistanceCheckerTest.java
├── scholarship
├── ScholarshipServiceTest.java
├── command
│ ├── adapter
│ │ ├── ApplyScholarshipControllerTest.java
│ │ └── ApplyScholarshipTDDTest.java
│ └── usecase
│ │ ├── ApplicationCheckerTest.java
│ │ ├── ApplyScholarshipServiceCodeFirstTest.java
│ │ └── ApplyScholarshipServiceTest.java
├── find_and_notify
│ └── FindTopAndNotifyServiceTest.java
└── notify
│ ├── SendResultEmailServiceSilentTest.java
│ └── SendResultEmailServiceTest.java
└── student
├── StudentTest.java
└── register
├── RegisterControllerTest.java
└── RegisterServiceTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | *.iml
3 | .idea
4 |
5 |
--------------------------------------------------------------------------------
/HELP.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | ### Reference Documentation
4 |
5 | For further reference, please consider the following sections:
6 |
7 | * [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
8 | * [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.5.3/maven-plugin/reference/html/)
9 | * [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.5.3/maven-plugin/reference/html/#build-image)
10 | * [Spring Web](https://docs.spring.io/spring-boot/docs/2.5.3/reference/htmlsingle/#boot-features-developing-web-applications)
11 |
12 | ### Guides
13 |
14 | The following guides illustrate how to use some features concretely:
15 |
16 | * [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
17 | * [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
18 | * [Building REST services with Spring](https://spring.io/guides/tutorials/bookmarks/)
19 |
20 |
--------------------------------------------------------------------------------
/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # https://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Maven Start Up Batch script
23 | #
24 | # Required ENV vars:
25 | # ------------------
26 | # JAVA_HOME - location of a JDK home dir
27 | #
28 | # Optional ENV vars
29 | # -----------------
30 | # M2_HOME - location of maven2's installed home dir
31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven
32 | # e.g. to debug Maven itself, use
33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files
35 | # ----------------------------------------------------------------------------
36 |
37 | if [ -z "$MAVEN_SKIP_RC" ] ; then
38 |
39 | if [ -f /etc/mavenrc ] ; then
40 | . /etc/mavenrc
41 | fi
42 |
43 | if [ -f "$HOME/.mavenrc" ] ; then
44 | . "$HOME/.mavenrc"
45 | fi
46 |
47 | fi
48 |
49 | # OS specific support. $var _must_ be set to either true or false.
50 | cygwin=false;
51 | darwin=false;
52 | mingw=false
53 | case "`uname`" in
54 | CYGWIN*) cygwin=true ;;
55 | MINGW*) mingw=true;;
56 | Darwin*) darwin=true
57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
59 | if [ -z "$JAVA_HOME" ]; then
60 | if [ -x "/usr/libexec/java_home" ]; then
61 | export JAVA_HOME="`/usr/libexec/java_home`"
62 | else
63 | export JAVA_HOME="/Library/Java/Home"
64 | fi
65 | fi
66 | ;;
67 | esac
68 |
69 | if [ -z "$JAVA_HOME" ] ; then
70 | if [ -r /etc/gentoo-release ] ; then
71 | JAVA_HOME=`java-config --jre-home`
72 | fi
73 | fi
74 |
75 | if [ -z "$M2_HOME" ] ; then
76 | ## resolve links - $0 may be a link to maven's home
77 | PRG="$0"
78 |
79 | # need this for relative symlinks
80 | while [ -h "$PRG" ] ; do
81 | ls=`ls -ld "$PRG"`
82 | link=`expr "$ls" : '.*-> \(.*\)$'`
83 | if expr "$link" : '/.*' > /dev/null; then
84 | PRG="$link"
85 | else
86 | PRG="`dirname "$PRG"`/$link"
87 | fi
88 | done
89 |
90 | saveddir=`pwd`
91 |
92 | M2_HOME=`dirname "$PRG"`/..
93 |
94 | # make it fully qualified
95 | M2_HOME=`cd "$M2_HOME" && pwd`
96 |
97 | cd "$saveddir"
98 | # echo Using m2 at $M2_HOME
99 | fi
100 |
101 | # For Cygwin, ensure paths are in UNIX format before anything is touched
102 | if $cygwin ; then
103 | [ -n "$M2_HOME" ] &&
104 | M2_HOME=`cygpath --unix "$M2_HOME"`
105 | [ -n "$JAVA_HOME" ] &&
106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
107 | [ -n "$CLASSPATH" ] &&
108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
109 | fi
110 |
111 | # For Mingw, ensure paths are in UNIX format before anything is touched
112 | if $mingw ; then
113 | [ -n "$M2_HOME" ] &&
114 | M2_HOME="`(cd "$M2_HOME"; pwd)`"
115 | [ -n "$JAVA_HOME" ] &&
116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
117 | fi
118 |
119 | if [ -z "$JAVA_HOME" ]; then
120 | javaExecutable="`which javac`"
121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
122 | # readlink(1) is not available as standard on Solaris 10.
123 | readLink=`which readlink`
124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
125 | if $darwin ; then
126 | javaHome="`dirname \"$javaExecutable\"`"
127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
128 | else
129 | javaExecutable="`readlink -f \"$javaExecutable\"`"
130 | fi
131 | javaHome="`dirname \"$javaExecutable\"`"
132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'`
133 | JAVA_HOME="$javaHome"
134 | export JAVA_HOME
135 | fi
136 | fi
137 | fi
138 |
139 | if [ -z "$JAVACMD" ] ; then
140 | if [ -n "$JAVA_HOME" ] ; then
141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
142 | # IBM's JDK on AIX uses strange locations for the executables
143 | JAVACMD="$JAVA_HOME/jre/sh/java"
144 | else
145 | JAVACMD="$JAVA_HOME/bin/java"
146 | fi
147 | else
148 | JAVACMD="`which java`"
149 | fi
150 | fi
151 |
152 | if [ ! -x "$JAVACMD" ] ; then
153 | echo "Error: JAVA_HOME is not defined correctly." >&2
154 | echo " We cannot execute $JAVACMD" >&2
155 | exit 1
156 | fi
157 |
158 | if [ -z "$JAVA_HOME" ] ; then
159 | echo "Warning: JAVA_HOME environment variable is not set."
160 | fi
161 |
162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
163 |
164 | # traverses directory structure from process work directory to filesystem root
165 | # first directory with .mvn subdirectory is considered project base directory
166 | find_maven_basedir() {
167 |
168 | if [ -z "$1" ]
169 | then
170 | echo "Path not specified to find_maven_basedir"
171 | return 1
172 | fi
173 |
174 | basedir="$1"
175 | wdir="$1"
176 | while [ "$wdir" != '/' ] ; do
177 | if [ -d "$wdir"/.mvn ] ; then
178 | basedir=$wdir
179 | break
180 | fi
181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc)
182 | if [ -d "${wdir}" ]; then
183 | wdir=`cd "$wdir/.."; pwd`
184 | fi
185 | # end of workaround
186 | done
187 | echo "${basedir}"
188 | }
189 |
190 | # concatenates all lines of a file
191 | concat_lines() {
192 | if [ -f "$1" ]; then
193 | echo "$(tr -s '\n' ' ' < "$1")"
194 | fi
195 | }
196 |
197 | BASE_DIR=`find_maven_basedir "$(pwd)"`
198 | if [ -z "$BASE_DIR" ]; then
199 | exit 1;
200 | fi
201 |
202 | ##########################################################################################
203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
204 | # This allows using the maven wrapper in projects that prohibit checking in binary data.
205 | ##########################################################################################
206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
207 | if [ "$MVNW_VERBOSE" = true ]; then
208 | echo "Found .mvn/wrapper/maven-wrapper.jar"
209 | fi
210 | else
211 | if [ "$MVNW_VERBOSE" = true ]; then
212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
213 | fi
214 | if [ -n "$MVNW_REPOURL" ]; then
215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
216 | else
217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
218 | fi
219 | while IFS="=" read key value; do
220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
221 | esac
222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
223 | if [ "$MVNW_VERBOSE" = true ]; then
224 | echo "Downloading from: $jarUrl"
225 | fi
226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
227 | if $cygwin; then
228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
229 | fi
230 |
231 | if command -v wget > /dev/null; then
232 | if [ "$MVNW_VERBOSE" = true ]; then
233 | echo "Found wget ... using wget"
234 | fi
235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
236 | wget "$jarUrl" -O "$wrapperJarPath"
237 | else
238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
239 | fi
240 | elif command -v curl > /dev/null; then
241 | if [ "$MVNW_VERBOSE" = true ]; then
242 | echo "Found curl ... using curl"
243 | fi
244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
245 | curl -o "$wrapperJarPath" "$jarUrl" -f
246 | else
247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
248 | fi
249 |
250 | else
251 | if [ "$MVNW_VERBOSE" = true ]; then
252 | echo "Falling back to using Java to download"
253 | fi
254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
255 | # For Cygwin, switch paths to Windows format before running javac
256 | if $cygwin; then
257 | javaClass=`cygpath --path --windows "$javaClass"`
258 | fi
259 | if [ -e "$javaClass" ]; then
260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
261 | if [ "$MVNW_VERBOSE" = true ]; then
262 | echo " - Compiling MavenWrapperDownloader.java ..."
263 | fi
264 | # Compiling the Java class
265 | ("$JAVA_HOME/bin/javac" "$javaClass")
266 | fi
267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
268 | # Running the downloader
269 | if [ "$MVNW_VERBOSE" = true ]; then
270 | echo " - Running MavenWrapperDownloader.java ..."
271 | fi
272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
273 | fi
274 | fi
275 | fi
276 | fi
277 | ##########################################################################################
278 | # End of extension
279 | ##########################################################################################
280 |
281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
282 | if [ "$MVNW_VERBOSE" = true ]; then
283 | echo $MAVEN_PROJECTBASEDIR
284 | fi
285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
286 |
287 | # For Cygwin, switch paths to Windows format before running java
288 | if $cygwin; then
289 | [ -n "$M2_HOME" ] &&
290 | M2_HOME=`cygpath --path --windows "$M2_HOME"`
291 | [ -n "$JAVA_HOME" ] &&
292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
293 | [ -n "$CLASSPATH" ] &&
294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
295 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
297 | fi
298 |
299 | # Provide a "standardized" way to retrieve the CLI args that will
300 | # work with both Windows and non-Windows executions.
301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
302 | export MAVEN_CMD_LINE_ARGS
303 |
304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
305 |
306 | exec "$JAVACMD" \
307 | $MAVEN_OPTS \
308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
311 |
--------------------------------------------------------------------------------
/mvnw.cmd:
--------------------------------------------------------------------------------
1 | @REM ----------------------------------------------------------------------------
2 | @REM Licensed to the Apache Software Foundation (ASF) under one
3 | @REM or more contributor license agreements. See the NOTICE file
4 | @REM distributed with this work for additional information
5 | @REM regarding copyright ownership. The ASF licenses this file
6 | @REM to you under the Apache License, Version 2.0 (the
7 | @REM "License"); you may not use this file except in compliance
8 | @REM with the License. You may obtain a copy of the License at
9 | @REM
10 | @REM https://www.apache.org/licenses/LICENSE-2.0
11 | @REM
12 | @REM Unless required by applicable law or agreed to in writing,
13 | @REM software distributed under the License is distributed on an
14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | @REM KIND, either express or implied. See the License for the
16 | @REM specific language governing permissions and limitations
17 | @REM under the License.
18 | @REM ----------------------------------------------------------------------------
19 |
20 | @REM ----------------------------------------------------------------------------
21 | @REM Maven Start Up Batch script
22 | @REM
23 | @REM Required ENV vars:
24 | @REM JAVA_HOME - location of a JDK home dir
25 | @REM
26 | @REM Optional ENV vars
27 | @REM M2_HOME - location of maven2's installed home dir
28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
31 | @REM e.g. to debug Maven itself, use
32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
34 | @REM ----------------------------------------------------------------------------
35 |
36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
37 | @echo off
38 | @REM set title of command window
39 | title %0
40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
42 |
43 | @REM set %HOME% to equivalent of $HOME
44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
45 |
46 | @REM Execute a user defined script before this one
47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
51 | :skipRcPre
52 |
53 | @setlocal
54 |
55 | set ERROR_CODE=0
56 |
57 | @REM To isolate internal variables from possible post scripts, we use another setlocal
58 | @setlocal
59 |
60 | @REM ==== START VALIDATION ====
61 | if not "%JAVA_HOME%" == "" goto OkJHome
62 |
63 | echo.
64 | echo Error: JAVA_HOME not found in your environment. >&2
65 | echo Please set the JAVA_HOME variable in your environment to match the >&2
66 | echo location of your Java installation. >&2
67 | echo.
68 | goto error
69 |
70 | :OkJHome
71 | if exist "%JAVA_HOME%\bin\java.exe" goto init
72 |
73 | echo.
74 | echo Error: JAVA_HOME is set to an invalid directory. >&2
75 | echo JAVA_HOME = "%JAVA_HOME%" >&2
76 | echo Please set the JAVA_HOME variable in your environment to match the >&2
77 | echo location of your Java installation. >&2
78 | echo.
79 | goto error
80 |
81 | @REM ==== END VALIDATION ====
82 |
83 | :init
84 |
85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
86 | @REM Fallback to current working directory if not found.
87 |
88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
90 |
91 | set EXEC_DIR=%CD%
92 | set WDIR=%EXEC_DIR%
93 | :findBaseDir
94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
95 | cd ..
96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
97 | set WDIR=%CD%
98 | goto findBaseDir
99 |
100 | :baseDirFound
101 | set MAVEN_PROJECTBASEDIR=%WDIR%
102 | cd "%EXEC_DIR%"
103 | goto endDetectBaseDir
104 |
105 | :baseDirNotFound
106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
107 | cd "%EXEC_DIR%"
108 |
109 | :endDetectBaseDir
110 |
111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
112 |
113 | @setlocal EnableExtensions EnableDelayedExpansion
114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
116 |
117 | :endReadAdditionalConfig
118 |
119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
122 |
123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
124 |
125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
127 | )
128 |
129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data.
131 | if exist %WRAPPER_JAR% (
132 | if "%MVNW_VERBOSE%" == "true" (
133 | echo Found %WRAPPER_JAR%
134 | )
135 | ) else (
136 | if not "%MVNW_REPOURL%" == "" (
137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
138 | )
139 | if "%MVNW_VERBOSE%" == "true" (
140 | echo Couldn't find %WRAPPER_JAR%, downloading it ...
141 | echo Downloading from: %DOWNLOAD_URL%
142 | )
143 |
144 | powershell -Command "&{"^
145 | "$webclient = new-object System.Net.WebClient;"^
146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
148 | "}"^
149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
150 | "}"
151 | if "%MVNW_VERBOSE%" == "true" (
152 | echo Finished downloading %WRAPPER_JAR%
153 | )
154 | )
155 | @REM End of extension
156 |
157 | @REM Provide a "standardized" way to retrieve the CLI args that will
158 | @REM work with both Windows and non-Windows executions.
159 | set MAVEN_CMD_LINE_ARGS=%*
160 |
161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
162 | if ERRORLEVEL 1 goto error
163 | goto end
164 |
165 | :error
166 | set ERROR_CODE=1
167 |
168 | :end
169 | @endlocal & set ERROR_CODE=%ERROR_CODE%
170 |
171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
175 | :skipRcPost
176 |
177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause
179 |
180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
181 |
182 | exit /B %ERROR_CODE%
183 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.5.3
9 |
10 |
11 | com.example
12 | itehlp2021
13 | 0.0.1-SNAPSHOT
14 | itehlp2021
15 | Demo project for Spring Boot
16 |
17 | 1.8
18 |
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter-web
23 |
24 |
25 |
26 | org.projectlombok
27 | lombok
28 | true
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-test
33 | test
34 |
35 |
36 | org.mockito
37 | mockito-inline
38 | test
39 |
40 |
41 |
42 |
43 | org.assertj
44 | assertj-core
45 | 3.20.2
46 | test
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | org.springframework.boot
55 | spring-boot-maven-plugin
56 | 2.5.3
57 |
58 |
59 |
60 | org.projectlombok
61 | lombok
62 |
63 |
64 |
65 |
66 |
67 |
68 | org.apache.maven.plugins
69 | maven-surefire-plugin
70 | 3.0.0-M5
71 |
72 | false
73 | 1
74 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/HelloController.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021;
2 |
3 | import org.springframework.web.bind.annotation.GetMapping;
4 | import org.springframework.web.bind.annotation.RestController;
5 |
6 | @RestController
7 | public class HelloController {
8 |
9 |
10 | @GetMapping("/")
11 | public String hello() {
12 | return "hello";
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/Itehlp2021Application.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class Itehlp2021Application {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(Itehlp2021Application.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/course/ClassRoom.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.course;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 |
6 | @Data
7 | @AllArgsConstructor
8 | public class ClassRoom {
9 |
10 | Position position;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/course/Course.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.course;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 |
6 | @Data
7 | @AllArgsConstructor
8 | public class Course {
9 | private ClassRoom classRoom;
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/course/CourseRepository.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.course;
2 |
3 | public class CourseRepository {
4 | public Course find(long courseId) {
5 | return null;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/course/DistanceCalculator.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.course;
2 |
3 | public class DistanceCalculator {
4 | public double calculate(Position position1, Position position2) {
5 | return 0;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/course/DistanceChecker.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.course;
2 |
3 |
4 | public class DistanceChecker {
5 |
6 | private final CourseRepository courseRepository;
7 | private final DistanceCalculator distanceCalculator;
8 |
9 | public DistanceChecker(CourseRepository courseRepository, DistanceCalculator distanceCalculator) {
10 | this.courseRepository = courseRepository;
11 | this.distanceCalculator = distanceCalculator;
12 | }
13 |
14 | public boolean checkDistance(long courseId, Position studentPosition) {
15 |
16 | Position classroomPosition = courseRepository.find(courseId).getClassRoom().getPosition();
17 |
18 | double distance = distanceCalculator.calculate(studentPosition, classroomPosition);
19 |
20 | return distance < 50D;
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/course/Position.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.course;
2 |
3 | public class Position {
4 | private final double longitude;
5 | private final double latitude;
6 |
7 | public Position(double longitude, double latitude) {
8 | this.longitude = longitude;
9 | this.latitude = latitude;
10 | }
11 |
12 | public double getLongitude() {
13 | return longitude;
14 | }
15 |
16 | public double getLatitude() {
17 | return latitude;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/BachelorScholarshipCalculator.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship;
2 |
3 | import java.util.List;
4 |
5 | public class BachelorScholarshipCalculator implements Calculator {
6 |
7 | @Override
8 | public int calculate(Transcript transcript) {
9 | List courses = transcript.getCourses();
10 | if (courses.isEmpty()) return 0; // 不修課跟人家領什麼獎學金!
11 |
12 | int total = courses.size();
13 | int achieved = 0;
14 | for (Course course : courses) {
15 | if (course.getScore() >= 80) {
16 | achieved++;
17 | }
18 | }
19 | double rate = (double) achieved / total;
20 |
21 | if (rate >= (double) 1 / 2) {
22 | return 10_000;
23 | } else if (rate >= (double) 1 / 3) {
24 | return 5_000;
25 | } else {
26 | return 0;
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/Calculator.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship;
2 |
3 | public interface Calculator {
4 | int calculate(Transcript transcript);
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/Course.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 |
6 | @Data
7 | @AllArgsConstructor
8 | public class Course {
9 | private String name;
10 | private int score;
11 | private int credit;
12 |
13 | public int getCredit() {
14 | return credit;
15 | }
16 |
17 | public void setCredit(int credit) {
18 | this.credit = credit;
19 | }
20 | }
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/MasterScholarshipCalculator.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship;
2 |
3 | public class MasterScholarshipCalculator implements Calculator {
4 |
5 | @Override
6 | public int calculate(Transcript transcript) {
7 |
8 | if (transcript.hasNoCourses()) return 0; // 不修課跟人家領什麼獎學金!
9 |
10 | double weightedAverage = transcript.calculateWeightedAverage();
11 |
12 | if (weightedAverage >= 90D) {
13 | return 15_000;
14 | } else if (weightedAverage >= 80D) {
15 | return 7_500;
16 | } else {
17 | return 0;
18 | }
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/PhDScholarshipCalculator.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship;
2 |
3 | import java.util.List;
4 |
5 | public class PhDScholarshipCalculator implements Calculator {
6 |
7 |
8 | @Override
9 | public int calculate(Transcript transcript) {
10 | List courses = transcript.getCourses();
11 | if (courses.isEmpty()) return 0; // 不修課跟人家領什麼獎學金!
12 |
13 |
14 | for (Course course : courses) {
15 | if (course.getScore() < 80) {
16 | return 0;
17 | }
18 | if (course.getScore() < 90) {
19 | return 20_000;
20 | }
21 | }
22 | return 40_000;
23 | }
24 | }
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/ScholarshipService.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship;
2 |
3 | public class ScholarshipService {
4 |
5 | public int calculate(Transcript transcript) throws UnknownProgramTypeException {
6 |
7 | Calculator calculator = findCalculator(transcript.getProgramType());
8 | return calculator.calculate(transcript);
9 |
10 | }
11 |
12 | private Calculator findCalculator(String programType) throws UnknownProgramTypeException {
13 | switch (programType) {
14 | case "Bachelor":
15 | return new BachelorScholarshipCalculator();
16 | case "Master":
17 | return new MasterScholarshipCalculator();
18 | case "PhD":
19 | return new PhDScholarshipCalculator();
20 | default:
21 | throw new UnknownProgramTypeException(programType);
22 | }
23 | }
24 |
25 |
26 | public static class UnknownProgramTypeException extends Throwable {
27 | public UnknownProgramTypeException(String programType) {
28 | super("Unknown program type: " + programType);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/Transcript.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.Arrays;
6 | import java.util.List;
7 |
8 | @Data
9 | public class Transcript {
10 |
11 | private long studentId;
12 | private String programType;
13 | private List courses;
14 |
15 | public Transcript(long studentId) {
16 | this.studentId = studentId;
17 | }
18 |
19 | public Transcript(String programType, Course... courses) {
20 | this.programType = programType;
21 | this.courses = Arrays.asList(courses);
22 | }
23 |
24 | public double calculateWeightedAverage() {
25 |
26 |
27 | double totalCredit = 0.001D;
28 | double totalWeightedScore = 0D;
29 |
30 | for (Course course : courses) {
31 | totalCredit += course.getCredit();
32 | totalWeightedScore += course.getScore() * course.getCredit();
33 | }
34 |
35 | double weightedAverage = totalWeightedScore / totalCredit;
36 | return weightedAverage;
37 | }
38 |
39 | boolean hasNoCourses() {
40 | return this.courses.isEmpty();
41 | }
42 |
43 | public String getProgramType() {
44 | return programType;
45 | }
46 |
47 | public void setProgramType(String programType) {
48 | this.programType = programType;
49 | }
50 | }
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/command/adapter/ApplicationForm.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.adapter;
2 |
3 | import idv.kuma.itehlp2021.scholarship.command.entity.Application;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Data;
6 |
7 | @Data
8 | @AllArgsConstructor
9 | public class ApplicationForm {
10 | private long studentId;
11 | private long scholarshipId;
12 |
13 | public Application toApplication() {
14 | return new Application(studentId, scholarshipId);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/command/adapter/ApplicationRepositoryImpl.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.adapter;
2 |
3 | import idv.kuma.itehlp2021.scholarship.command.entity.Application;
4 | import idv.kuma.itehlp2021.scholarship.command.usecase.ApplicationRepository;
5 | import org.springframework.stereotype.Component;
6 |
7 | @Component
8 | public class ApplicationRepositoryImpl implements ApplicationRepository {
9 | @Override
10 | public void create(ApplicationForm application) {
11 | // do nothing
12 | }
13 |
14 | @Override
15 | public void create(Application application) {
16 | // do nothing
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/command/adapter/ApplyScholarshipController.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.adapter;
2 |
3 | import idv.kuma.itehlp2021.scholarship.command.usecase.ApplyScholarshipService;
4 | import idv.kuma.itehlp2021.student.register.ApiResponse;
5 | import lombok.extern.slf4j.Slf4j;
6 | import org.springframework.http.ResponseEntity;
7 | import org.springframework.web.bind.annotation.PostMapping;
8 | import org.springframework.web.bind.annotation.RequestBody;
9 | import org.springframework.web.bind.annotation.RestController;
10 |
11 | @RestController
12 | @Slf4j
13 | public class ApplyScholarshipController {
14 |
15 | ApplyScholarshipService applyScholarshipService;
16 |
17 | public ApplyScholarshipController(ApplyScholarshipService applyScholarshipService) {
18 | this.applyScholarshipService = applyScholarshipService;
19 | }
20 |
21 | @PostMapping("/scholarship/apply")
22 | public ResponseEntity apply(@RequestBody ApplicationForm applicationForm) {
23 | try {
24 | applyScholarshipService.apply(applicationForm);
25 | } catch (ClientSideErrorException e) {
26 | return ResponseEntity.status(400).body(ApiResponse.bad(e.getCode()));
27 | } catch (ServerSideErrorException e) {
28 | return ResponseEntity.status(500).body(ApiResponse.bad(666));
29 | } catch (Exception e) {
30 | return ResponseEntity.status(500).body(ApiResponse.bad(999));
31 | }
32 |
33 | return ResponseEntity.status(200).body(ApiResponse.empty());
34 |
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/command/adapter/ClientSideErrorException.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.adapter;
2 |
3 | public class ClientSideErrorException extends Exception {
4 | private final int code;
5 |
6 | public ClientSideErrorException(String message, int code) {
7 | super(message);
8 | this.code = code;
9 | }
10 |
11 | public int getCode() {
12 | return this.code;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/command/adapter/ScholarshipRepositoryImpl.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.adapter;
2 |
3 | import idv.kuma.itehlp2021.scholarship.command.entity.Scholarship;
4 | import idv.kuma.itehlp2021.scholarship.command.usecase.RepositoryAccessDataFailException;
5 | import idv.kuma.itehlp2021.scholarship.command.usecase.ScholarshipRepository;
6 | import org.springframework.stereotype.Component;
7 |
8 | import java.util.Optional;
9 |
10 | @Component
11 | public class ScholarshipRepositoryImpl implements ScholarshipRepository {
12 | @Override
13 | public Scholarship find(long scholarshipId) {
14 | return null;
15 | }
16 |
17 | @Override
18 | public Optional findOptional(long scholarshipId) throws RepositoryAccessDataFailException {
19 | return null;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/command/adapter/ServerSideErrorException.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.adapter;
2 |
3 | public class ServerSideErrorException extends Exception {
4 | private int code;
5 |
6 | public ServerSideErrorException(String message) {
7 | super(message);
8 | }
9 |
10 | public ServerSideErrorException(String message, int code) {
11 | super(message);
12 | this.code = code;
13 | }
14 |
15 | public int getCode() {
16 | return code;
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/command/entity/Application.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.entity;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class Application {
7 | private final long studentId;
8 | private final long scholarshipId;
9 |
10 | public Application(long studentId, long scholarshipId) {
11 |
12 | this.studentId = studentId;
13 | this.scholarshipId = scholarshipId;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/command/entity/Scholarship.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.entity;
2 |
3 | import idv.kuma.itehlp2021.student.Student;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Data;
6 |
7 | import java.time.LocalDate;
8 |
9 | @Data
10 | @AllArgsConstructor
11 | public class Scholarship {
12 | private LocalDate deadline;
13 |
14 | public boolean checkQualification(Student student) {
15 | return student.getProgram().equals("PhD");
16 | }
17 |
18 | public boolean checkDeadline() {
19 | LocalDate deadline = getDeadline();
20 | LocalDate now = LocalDate.now();
21 | boolean isOverTime = now.isAfter(deadline);
22 | return isOverTime;
23 | }
24 |
25 | public LocalDate getDeadline() {
26 | return deadline;
27 | }
28 |
29 | public void setDeadline(LocalDate deadline) {
30 | this.deadline = deadline;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/command/usecase/ApplicationChecker.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.usecase;
2 |
3 |
4 | import idv.kuma.itehlp2021.scholarship.command.adapter.ApplicationForm;
5 | import idv.kuma.itehlp2021.scholarship.command.adapter.ScholarshipRepositoryImpl;
6 | import idv.kuma.itehlp2021.scholarship.command.entity.Scholarship;
7 | import org.springframework.stereotype.Component;
8 |
9 | import java.time.LocalDate;
10 |
11 | @Component
12 | public class ApplicationChecker {
13 |
14 | private final ScholarshipRepositoryImpl scholarshipRepository;
15 |
16 | public ApplicationChecker(ScholarshipRepositoryImpl scholarshipRepository) {
17 | this.scholarshipRepository = scholarshipRepository;
18 | }
19 |
20 | public boolean checkTime(ApplicationForm applicationForm) {
21 |
22 | Scholarship scholarship = scholarshipRepository.find(applicationForm.getScholarshipId());
23 |
24 | LocalDate deadline = scholarship.getDeadline();
25 |
26 | LocalDate today = LocalDate.now();
27 |
28 | return today.isEqual(deadline)
29 | || today.isBefore(deadline);
30 |
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/command/usecase/ApplicationRepository.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.usecase;
2 |
3 |
4 | import idv.kuma.itehlp2021.scholarship.command.adapter.ApplicationForm;
5 | import idv.kuma.itehlp2021.scholarship.command.entity.Application;
6 |
7 | public interface ApplicationRepository {
8 | void create(ApplicationForm application);
9 |
10 | void create(Application application) throws RepositoryAccessDataFailException;
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/command/usecase/ApplyScholarshipService.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.usecase;
2 |
3 | import idv.kuma.itehlp2021.scholarship.command.adapter.ApplicationForm;
4 | import idv.kuma.itehlp2021.scholarship.command.adapter.ClientSideErrorException;
5 | import idv.kuma.itehlp2021.scholarship.command.adapter.ServerSideErrorException;
6 | import idv.kuma.itehlp2021.scholarship.command.entity.Application;
7 | import idv.kuma.itehlp2021.scholarship.command.entity.Scholarship;
8 | import idv.kuma.itehlp2021.student.Student;
9 | import idv.kuma.itehlp2021.student.register.StudentRepository;
10 | import org.springframework.stereotype.Component;
11 |
12 | @Component
13 | public class ApplyScholarshipService {
14 |
15 | private final ScholarshipRepository scholarshipRepository;
16 | private final ApplicationRepository applicationRepository;
17 | private final StudentRepository studentRepository;
18 |
19 |
20 | public ApplyScholarshipService(StudentRepository studentRepository, ScholarshipRepository scholarshipRepository, ApplicationRepository applicationRepository) {
21 | this.studentRepository = studentRepository;
22 | this.scholarshipRepository = scholarshipRepository;
23 | this.applicationRepository = applicationRepository;
24 | }
25 |
26 | public void apply(ApplicationForm applicationForm) throws ClientSideErrorException, ServerSideErrorException {
27 |
28 | // 調閱學生資料
29 | Student student = findStudent(applicationForm);
30 |
31 | // 調閱獎學金規定的資料
32 | Scholarship scholarship = findScholarship(applicationForm);
33 |
34 | // 查驗是否符合資格
35 | checkDeadline(scholarship);
36 |
37 | // 查驗是否符合資格
38 | checkProgramIsPhD(scholarship, student);
39 |
40 | // 填寫正式申請書
41 | Application application = applicationForm.toApplication();
42 |
43 | // 存檔
44 | // 思考題:萬一 create 時,DB 資料已存在,該怎麼辦?
45 | createApplication(application);
46 |
47 | }
48 |
49 | private Student findStudent(ApplicationForm applicationForm) throws ClientSideErrorException, ServerSideErrorException {
50 | try {
51 | return studentRepository.find(applicationForm.getStudentId())
52 | .orElseThrow(() -> new ClientSideErrorException("cannot find student", 987));
53 | } catch (RepositoryAccessDataFailException e) {
54 | throw new ServerSideErrorException("failed to retrieve student data", 666);
55 | }
56 | }
57 |
58 | private Scholarship findScholarship(ApplicationForm applicationForm) throws ClientSideErrorException, ServerSideErrorException {
59 | try {
60 | return scholarshipRepository.findOptional(applicationForm.getScholarshipId())
61 | .orElseThrow(() -> new ClientSideErrorException("cannot find scholarship", 369));
62 | } catch (RepositoryAccessDataFailException e) {
63 | throw new ServerSideErrorException("failed to retrieve scholarship data", 666);
64 | }
65 | }
66 |
67 | private void checkDeadline(Scholarship scholarship) throws ClientSideErrorException {
68 |
69 | if (scholarship.checkDeadline()) {
70 | throw new ClientSideErrorException("application over time", 374);
71 | }
72 | }
73 |
74 | private void checkProgramIsPhD(Scholarship scholarship, Student student) throws ClientSideErrorException {
75 | if (!scholarship.checkQualification(student)) {
76 | throw new ClientSideErrorException("this scholarship is for PhD students only", 375);
77 | }
78 | }
79 |
80 | private void createApplication(Application application) throws ServerSideErrorException {
81 | try {
82 | this.applicationRepository.create(application);
83 | } catch (RepositoryAccessDataFailException e) {
84 | throw new ServerSideErrorException("failed to create application", 666);
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/command/usecase/ApplyScholarshipServiceCodeFirst.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.usecase;
2 |
3 | import idv.kuma.itehlp2021.scholarship.command.adapter.ApplicationForm;
4 | import idv.kuma.itehlp2021.scholarship.command.adapter.ClientSideErrorException;
5 | import idv.kuma.itehlp2021.scholarship.command.adapter.ServerSideErrorException;
6 |
7 |
8 | public class ApplyScholarshipServiceCodeFirst {
9 |
10 | private final ApplicationChecker checker;
11 | private final ApplicationRepository applicationRepository;
12 |
13 | public ApplyScholarshipServiceCodeFirst(ApplicationChecker checker, ApplicationRepository applicationRepository) {
14 | this.checker = checker;
15 | this.applicationRepository = applicationRepository;
16 | }
17 |
18 |
19 | public void apply(ApplicationForm applicationForm) throws ClientSideErrorException, ServerSideErrorException {
20 | if (this.checker.checkTime(applicationForm)) {
21 |
22 | this.applicationRepository.create(applicationForm);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/command/usecase/RepositoryAccessDataFailException.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.usecase;
2 |
3 | public class RepositoryAccessDataFailException extends Exception {
4 | }
5 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/command/usecase/ScholarshipRepository.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.usecase;
2 |
3 | import idv.kuma.itehlp2021.scholarship.command.entity.Scholarship;
4 |
5 | import java.util.Optional;
6 |
7 | public interface ScholarshipRepository {
8 | Scholarship find(long scholarshipId);
9 |
10 | Optional findOptional(long scholarshipId) throws RepositoryAccessDataFailException;
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/find_and_notify/FindTopAndNotifyService.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.find_and_notify;
2 |
3 | import idv.kuma.itehlp2021.scholarship.Transcript;
4 | import idv.kuma.itehlp2021.scholarship.notify.SendResultEmailService;
5 |
6 | import java.util.List;
7 |
8 | public class FindTopAndNotifyService {
9 |
10 | private TranscriptRepository repository;
11 | private SendResultEmailService emailService;
12 |
13 | public FindTopAndNotifyService(TranscriptRepository repository, SendResultEmailService emailService) {
14 | this.repository = repository;
15 | this.emailService = emailService;
16 | }
17 |
18 | public void execute(String semester, long courseId) {
19 |
20 | List transcripts = repository.findHighestScore(semester, courseId);
21 |
22 | for (Transcript transcript : transcripts) {
23 |
24 | long studentId = transcript.getStudentId();
25 |
26 | this.emailService.send(studentId, "Congratulations! You've got Scholarship");
27 |
28 | }
29 |
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/find_and_notify/TranscriptRepository.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.find_and_notify;
2 |
3 | import idv.kuma.itehlp2021.scholarship.Transcript;
4 |
5 | import java.util.Collections;
6 | import java.util.List;
7 |
8 | public class TranscriptRepository {
9 | public List findHighestScore(String semester, long courseId) {
10 | return Collections.emptyList();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/notify/Mailer.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.notify;
2 |
3 | public interface Mailer {
4 | boolean send(ScholarshipResult result);
5 |
6 | void silentSend(ScholarshipResult result);
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/notify/ScholarshipResult.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.notify;
2 |
3 | public class ScholarshipResult {
4 | }
5 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/scholarship/notify/SendResultEmailService.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.notify;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.concurrent.ExecutorService;
6 | import java.util.concurrent.Executors;
7 | import java.util.concurrent.Future;
8 |
9 | public class SendResultEmailService {
10 |
11 | private final ExecutorService executorService;
12 | private final Mailer mailer;
13 |
14 | public SendResultEmailService(Mailer mailer, ExecutorService executorService) {
15 | this.mailer = mailer;
16 | this.executorService = executorService;
17 |
18 | }
19 |
20 | public SendResultEmailService(Mailer mailer) {
21 | this.mailer = mailer;
22 | executorService = Executors.newFixedThreadPool(300);
23 | }
24 |
25 | public List> send(List results) {
26 |
27 | List> futures = new ArrayList<>();
28 |
29 | for (ScholarshipResult result : results) {
30 | futures.add(executorService.submit(() -> mailer.send(result)));
31 | }
32 |
33 | return futures;
34 |
35 | }
36 |
37 | public void silentSend(List results) {
38 |
39 | for (ScholarshipResult result : results) {
40 | executorService.submit(
41 | () -> mailer.silentSend(result)
42 | );
43 | }
44 |
45 | }
46 |
47 | public void send(long studentId, String message) {
48 | // do nothing (TBD)
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/student/Student.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.student;
2 |
3 | import lombok.Data;
4 |
5 | @Data
6 | public class Student {
7 |
8 | String lastName;
9 | String firstName;
10 | private String program = "unassigned";
11 | private Student student;
12 |
13 | public Student(String firstName, String lastName) {
14 | this.lastName = lastName;
15 | this.firstName = firstName;
16 | }
17 |
18 | public Student(String firstName, String lastName, String program) {
19 | this.firstName = firstName;
20 | this.lastName = lastName;
21 | this.program = program;
22 | }
23 |
24 | public String getFullName() {
25 | return firstName + " " + lastName;
26 | }
27 |
28 | }
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/student/register/ApiResponse.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.student.register;
2 |
3 |
4 | import lombok.AllArgsConstructor;
5 | import lombok.Data;
6 |
7 | @Data
8 | @AllArgsConstructor
9 | public class ApiResponse {
10 | private Integer errorCode;
11 |
12 |
13 | public static ApiResponse empty() {
14 | return new ApiResponse(0);
15 | }
16 |
17 | public static ApiResponse bad(int errorCode) {
18 | return new ApiResponse(errorCode);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/student/register/DataNotFoundException.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.student.register;
2 |
3 | public class DataNotFoundException extends Exception {
4 | }
5 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/student/register/RegisterController.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.student.register;
2 |
3 | import lombok.extern.slf4j.Slf4j;
4 | import org.springframework.http.ResponseEntity;
5 | import org.springframework.web.bind.annotation.PostMapping;
6 | import org.springframework.web.bind.annotation.RequestBody;
7 | import org.springframework.web.bind.annotation.RestController;
8 |
9 |
10 | @RestController
11 | @Slf4j
12 | public class RegisterController {
13 |
14 | private final RegisterService service;
15 |
16 | public RegisterController(RegisterService service) {
17 | this.service = service;
18 | }
19 |
20 | @PostMapping("/register")
21 | public ResponseEntity register(@RequestBody RegisterRequest request) {
22 | try {
23 | service.execute(request);
24 |
25 | // ApiResponse 是前後端共同講好的回傳資料格式
26 | // 這裡與前端說好,如果成功就直接回 200 就好,內容可以「空白」
27 | return ResponseEntity.ok(ApiResponse.empty());
28 | } catch (StudentNotExistException e) {
29 | // 我們不認為這是系統出錯,所以寫 info 就好,以免過多 error 在 log 裡干擾閱讀
30 | log.info("Student not found. " + e.getMessage());
31 |
32 | // 用 Http 400 告訴前端這是用戶有問題,
33 | // 並在 body 裡帶一個特殊代碼「987」,代表「用戶不存在」
34 | return ResponseEntity.status(400).body(ApiResponse.bad(987));
35 | }
36 | }
37 | }
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/student/register/RegisterRequest.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.student.register;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 |
8 | @Data
9 | @AllArgsConstructor
10 | @NoArgsConstructor
11 | public class RegisterRequest {
12 | private long id;
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/student/register/RegisterService.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.student.register;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.stereotype.Component;
5 |
6 | @Component
7 | public class RegisterService {
8 |
9 | private final StudentRepository repository;
10 |
11 | @Autowired
12 | public RegisterService(StudentRepository repository) {
13 | this.repository = repository;
14 | }
15 |
16 | public void execute(RegisterRequest request) throws StudentNotExistException {
17 | try {
18 | repository.register(request);
19 | } catch (DataNotFoundException e) {
20 | throw new StudentNotExistException("Student not exists", e);
21 | }
22 |
23 | }
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/student/register/StudentNotExistException.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.student.register;
2 |
3 | public class StudentNotExistException extends Exception {
4 | public StudentNotExistException(String message) {
5 | super(message);
6 | }
7 |
8 | public StudentNotExistException(String message, DataNotFoundException e) {
9 | super(message, e);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/student/register/StudentRepository.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.student.register;
2 |
3 | import idv.kuma.itehlp2021.scholarship.command.usecase.RepositoryAccessDataFailException;
4 | import idv.kuma.itehlp2021.student.Student;
5 |
6 | import java.util.Optional;
7 |
8 | public interface StudentRepository {
9 | void register(RegisterRequest request) throws DataNotFoundException;
10 |
11 | Optional find(long studentId) throws RepositoryAccessDataFailException;
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/idv/kuma/itehlp2021/student/register/StudentRepositoryImpl.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.student.register;
2 |
3 |
4 | import idv.kuma.itehlp2021.scholarship.command.usecase.RepositoryAccessDataFailException;
5 | import idv.kuma.itehlp2021.student.Student;
6 | import org.springframework.stereotype.Component;
7 |
8 | import java.util.Optional;
9 |
10 | @Component
11 | public class StudentRepositoryImpl implements StudentRepository {
12 | @Override
13 | public void register(RegisterRequest request) throws DataNotFoundException {
14 | // do nothing
15 | }
16 |
17 | @Override
18 | public Optional find(long studentId) throws RepositoryAccessDataFailException {
19 | return null;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/test/java/idv/kuma/itehlp2021/Itehlp2021ApplicationTests.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
6 | import org.springframework.boot.test.context.SpringBootTest;
7 | import org.springframework.test.web.servlet.MockMvc;
8 | import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
9 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
10 |
11 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
12 |
13 | @SpringBootTest
14 | @AutoConfigureMockMvc
15 | class Itehlp2021ApplicationTests {
16 |
17 | @Autowired
18 | MockMvc mockMvc;
19 |
20 |
21 | @Test
22 | void contextLoads() throws Exception {
23 |
24 | mockMvc.perform(MockMvcRequestBuilders.get("/"))
25 | .andExpect(status().is(200));
26 |
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/test/java/idv/kuma/itehlp2021/course/DistanceCheckerTest.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.course;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.mockito.Mockito;
5 |
6 | import static org.junit.jupiter.api.Assertions.assertFalse;
7 | import static org.junit.jupiter.api.Assertions.assertTrue;
8 |
9 | class DistanceCheckerTest {
10 | @Test
11 | void closed_enough() {
12 |
13 | Position studentPosition = new Position(1D, 1D);
14 | Position classroomPosition = new Position(0D, 0D);
15 |
16 | DistanceChecker distanceChecker = new DistanceChecker(
17 | dummy_repository(9527L, classroomPosition),
18 | dummy_calculator(49D, studentPosition, classroomPosition));
19 |
20 | assertTrue(distanceChecker.checkDistance(9527L, studentPosition));
21 |
22 | }
23 |
24 | private CourseRepository dummy_repository(long courseId, Position position) {
25 | Course course = new Course(new ClassRoom(position));
26 |
27 | CourseRepository repository = Mockito.mock(CourseRepository.class);
28 | Mockito.when(repository.find(courseId))
29 | .thenReturn(course);
30 | return repository;
31 | }
32 |
33 | private DistanceCalculator dummy_calculator(double distance, Position position1, Position position2) {
34 | DistanceCalculator calculator = Mockito.mock(DistanceCalculator.class);
35 | Mockito.when(calculator.calculate(position1, position2))
36 | .thenReturn(distance);
37 | return calculator;
38 | }
39 |
40 | @Test
41 | void too_far() {
42 | Position studentPosition = new Position(99D, 99D);
43 | Position classroomPosition = new Position(0D, 0D);
44 |
45 | DistanceChecker distanceChecker = new DistanceChecker(
46 | dummy_repository(9527L, classroomPosition),
47 | dummy_calculator(51D, studentPosition, classroomPosition));
48 |
49 | assertFalse(distanceChecker.checkDistance(9527L, studentPosition));
50 |
51 | }
52 | }
--------------------------------------------------------------------------------
/src/test/java/idv/kuma/itehlp2021/scholarship/ScholarshipServiceTest.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.Test;
5 |
6 | class ScholarshipServiceTest {
7 |
8 | @Test
9 | void bachelor_full_scholarship() throws ScholarshipService.UnknownProgramTypeException {
10 |
11 | ScholarshipService service = new ScholarshipService();
12 |
13 | int actual = service.calculate(
14 | new Transcript(
15 | "Bachelor",
16 | new Course("Algorithm", 70, 3),
17 | new Course("Computer Internet", 80, 2),
18 | new Course("Operating System", 90, 3)
19 | ));
20 |
21 | Assertions.assertEquals(10_000, actual);
22 | }
23 |
24 | @Test
25 | void bachelor_half_scholarship() throws ScholarshipService.UnknownProgramTypeException {
26 |
27 | ScholarshipService service = new ScholarshipService();
28 |
29 | int actual = service.calculate(new Transcript(
30 | "Bachelor",
31 | new Course("Algorithm", 70, 3),
32 | new Course("Computer Internet", 70, 2),
33 | new Course("Operating System", 90, 3)
34 |
35 | ));
36 |
37 | Assertions.assertEquals(5_000, actual);
38 | }
39 |
40 | @Test
41 | void bachelor_NO_courses() throws ScholarshipService.UnknownProgramTypeException {
42 |
43 | ScholarshipService service = new ScholarshipService();
44 |
45 | int actual = service.calculate(new Transcript("Bachelor"));
46 |
47 | Assertions.assertEquals(0, actual);
48 | }
49 |
50 | @Test
51 | void bachelor_NO_scholarship() throws ScholarshipService.UnknownProgramTypeException {
52 |
53 | ScholarshipService service = new ScholarshipService();
54 |
55 | int actual = service.calculate(new Transcript(
56 | "Bachelor",
57 | new Course("Algorithm", 70, 3),
58 | new Course("Computer Internet", 70, 2),
59 | new Course("Operating System", 70, 3)
60 |
61 | ));
62 |
63 | Assertions.assertEquals(0, actual);
64 | }
65 |
66 |
67 | ///
68 |
69 | @Test
70 | void master_full_scholarship() throws ScholarshipService.UnknownProgramTypeException {
71 |
72 | ScholarshipService service = new ScholarshipService();
73 |
74 | int actual = service.calculate(
75 | new Transcript(
76 | "Master",
77 | new Course("Algorithm", 99, 3),
78 | new Course("Computer Internet", 89, 2),
79 | new Course("Operating System", 99, 3)
80 | ));
81 |
82 | Assertions.assertEquals(15_000, actual);
83 | }
84 |
85 | @Test
86 | void master_half_scholarship() throws ScholarshipService.UnknownProgramTypeException {
87 |
88 | ScholarshipService service = new ScholarshipService();
89 |
90 | int actual = service.calculate(new Transcript(
91 | "Master",
92 | new Course("Algorithm", 89, 3),
93 | new Course("Computer Internet", 79, 2),
94 | new Course("Operating System", 89, 3)
95 |
96 | ));
97 |
98 | Assertions.assertEquals(7_500, actual);
99 | }
100 |
101 | @Test
102 | void master_NO_courses() throws ScholarshipService.UnknownProgramTypeException {
103 |
104 | ScholarshipService service = new ScholarshipService();
105 |
106 | int actual = service.calculate(new Transcript("Master"));
107 |
108 | Assertions.assertEquals(0, actual);
109 | }
110 |
111 | @Test
112 | void master_NO_scholarship() throws ScholarshipService.UnknownProgramTypeException {
113 |
114 | ScholarshipService service = new ScholarshipService();
115 |
116 | int actual = service.calculate(new Transcript(
117 | "Master",
118 | new Course("Algorithm", 79, 3),
119 | new Course("Computer Internet", 79, 2),
120 | new Course("Operating System", 79, 3)
121 |
122 | ));
123 |
124 | Assertions.assertEquals(0, actual);
125 | }
126 |
127 |
128 | //////
129 | @Test
130 | void PhD_full_scholarship() throws ScholarshipService.UnknownProgramTypeException {
131 |
132 | ScholarshipService service = new ScholarshipService();
133 |
134 | int actual = service.calculate(
135 | new Transcript(
136 | "PhD",
137 | new Course("Algorithm", 90, 3),
138 | new Course("Computer Internet", 90, 2),
139 | new Course("Operating System", 90, 3)
140 | ));
141 |
142 | Assertions.assertEquals(40_000, actual);
143 | }
144 |
145 | @Test
146 | void PhD_half_scholarship() throws ScholarshipService.UnknownProgramTypeException {
147 |
148 | ScholarshipService service = new ScholarshipService();
149 |
150 | int actual = service.calculate(new Transcript(
151 | "PhD",
152 | new Course("Algorithm", 80, 3),
153 | new Course("Computer Internet", 80, 2),
154 | new Course("Operating System", 80, 3)
155 |
156 | ));
157 |
158 | Assertions.assertEquals(20_000, actual);
159 | }
160 |
161 | @Test
162 | void PhD_NO_courses() throws ScholarshipService.UnknownProgramTypeException {
163 |
164 | ScholarshipService service = new ScholarshipService();
165 |
166 | int actual = service.calculate(new Transcript("PhD"));
167 |
168 | Assertions.assertEquals(0, actual);
169 | }
170 |
171 | @Test
172 | void PhD_NO_scholarship() throws ScholarshipService.UnknownProgramTypeException {
173 |
174 | ScholarshipService service = new ScholarshipService();
175 |
176 | int actual = service.calculate(new Transcript(
177 | "PhD",
178 | new Course("Algorithm", 79, 3),
179 | new Course("Computer Internet", 79, 2),
180 | new Course("Operating System", 79, 3)
181 |
182 | ));
183 |
184 | Assertions.assertEquals(0, actual);
185 | }
186 |
187 | /////////
188 | @Test
189 | void WRONG_type() throws ScholarshipService.UnknownProgramTypeException {
190 |
191 | ScholarshipService service = new ScholarshipService();
192 |
193 | try {
194 | service.calculate(new Transcript(
195 | "WRONG_TYPE",
196 | new Course("Algorithm", 100, 3),
197 | new Course("Computer Internet", 100, 2),
198 | new Course("Operating System", 100, 3)
199 |
200 | ));
201 | } catch (ScholarshipService.UnknownProgramTypeException e) {
202 | Assertions.assertTrue(e.getMessage().contains("WRONG_TYPE"));
203 | }
204 |
205 | }
206 | }
--------------------------------------------------------------------------------
/src/test/java/idv/kuma/itehlp2021/scholarship/command/adapter/ApplyScholarshipControllerTest.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.adapter;
2 |
3 | import com.fasterxml.jackson.core.JsonProcessingException;
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import idv.kuma.itehlp2021.scholarship.command.usecase.ApplyScholarshipService;
6 | import idv.kuma.itehlp2021.student.register.ApiResponse;
7 | import org.junit.jupiter.api.Test;
8 | import org.mockito.Mockito;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
11 | import org.springframework.boot.test.context.SpringBootTest;
12 | import org.springframework.boot.test.mock.mockito.MockBean;
13 | import org.springframework.http.MediaType;
14 | import org.springframework.test.web.servlet.MockMvc;
15 | import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
16 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
17 |
18 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
19 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
20 |
21 | @SpringBootTest
22 | @AutoConfigureMockMvc
23 | class ApplyScholarshipControllerTest {
24 |
25 | private final ObjectMapper objectMapper = new ObjectMapper();
26 | @MockBean
27 | private ApplyScholarshipService applyScholarshipService;
28 | @Autowired
29 | private MockMvc mockMvc;
30 |
31 | @Test
32 | void all_ok() throws Exception {
33 |
34 | mockMvc.perform(request(
35 | "/scholarship/apply"
36 | , application_form(9527L, 55688L)))
37 | .andExpect(status().is(200))
38 | .andExpect(content().json(objectMapper.writeValueAsString(ApiResponse.empty())));
39 |
40 | }
41 |
42 | private MockHttpServletRequestBuilder request(String urlTemplate, ApplicationForm form) throws JsonProcessingException {
43 | MockHttpServletRequestBuilder request = MockMvcRequestBuilders
44 | .post(urlTemplate)
45 | .contentType(MediaType.APPLICATION_JSON)
46 | .content(objectMapper.writeValueAsString(form));
47 | return request;
48 | }
49 |
50 | private ApplicationForm application_form(long studentId, long scholarshipId) {
51 | ApplicationForm applicationForm = new ApplicationForm(
52 | studentId,
53 | scholarshipId
54 | );
55 | return applicationForm;
56 | }
57 |
58 | @Test
59 | void student_NOT_exists() throws Exception {
60 |
61 | assume_student_not_exist(9527L);
62 |
63 | mockMvc.perform(request(
64 | "/scholarship/apply"
65 | , application_form(9527L, 55688L)))
66 | .andExpect(status().is(400))
67 | .andExpect(content().json(bad_response_content(987)));
68 |
69 | }
70 |
71 | private void assume_student_not_exist(long studentId) throws ServerSideErrorException, ClientSideErrorException {
72 | Mockito.doThrow(new ClientSideErrorException("ANY_MESSAGE", 987))
73 | .when(applyScholarshipService)
74 | .apply(application_form(studentId, 55688L));
75 | }
76 |
77 | private String bad_response_content(int errorCode) throws JsonProcessingException {
78 | return objectMapper.writeValueAsString(ApiResponse.bad(errorCode));
79 | }
80 |
81 | @Test
82 | void scholarship_NOT_exists() throws Exception {
83 |
84 | assume_scholarship_not_exists(55688L);
85 |
86 | mockMvc.perform(request(
87 | "/scholarship/apply"
88 | , application_form(9527L, 55688L)))
89 | .andExpect(status().is(400))
90 | .andExpect(content().json(bad_response_content(369)));// 369: scholar not exists
91 |
92 | }
93 |
94 | private void assume_scholarship_not_exists(long scholarshipId) throws ServerSideErrorException, ClientSideErrorException {
95 | Mockito.doThrow(new ClientSideErrorException("ANY_MESSAGE", 369))
96 | .when(applyScholarshipService)
97 | .apply(application_form(9527L, scholarshipId));
98 | }
99 |
100 | @Test
101 | void data_access_error() throws Exception {
102 |
103 | assume_data_access_would_fail(9527L, 55688L);
104 |
105 | mockMvc.perform(request(
106 | "/scholarship/apply"
107 | , application_form(9527L, 55688L)))
108 | .andExpect(status().is(500))
109 | .andExpect(content().json(bad_response_content(666)));// 666: data access error
110 |
111 | }
112 |
113 | private void assume_data_access_would_fail(long studentId, long scholarshipId) throws ServerSideErrorException, ClientSideErrorException {
114 | Mockito.doThrow(new ServerSideErrorException("ANY_MESSAGE"))
115 | .when(applyScholarshipService)
116 | .apply(application_form(studentId, scholarshipId));
117 | }
118 |
119 | @Test
120 | void unknown_error() throws Exception {
121 |
122 | given_some_bug_exists(9527L, 55688L);
123 |
124 | mockMvc.perform(request(
125 | "/scholarship/apply"
126 | , application_form(9527L, 55688L)))
127 | .andExpect(status().is(500))
128 | .andExpect(content().json(bad_response_content(999)));
129 |
130 | }
131 |
132 | private void given_some_bug_exists(long studentId, long scholarshipId) throws ClientSideErrorException, ServerSideErrorException {
133 | Mockito.doThrow(new RuntimeException("some message"))
134 | .when(applyScholarshipService)
135 | .apply(application_form(studentId, scholarshipId));
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/test/java/idv/kuma/itehlp2021/scholarship/command/adapter/ApplyScholarshipTDDTest.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.adapter;
2 |
3 | public class ApplyScholarshipTDDTest {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/test/java/idv/kuma/itehlp2021/scholarship/command/usecase/ApplicationCheckerTest.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.usecase;
2 |
3 | import idv.kuma.itehlp2021.scholarship.command.adapter.ApplicationForm;
4 | import idv.kuma.itehlp2021.scholarship.command.adapter.ScholarshipRepositoryImpl;
5 | import idv.kuma.itehlp2021.scholarship.command.entity.Scholarship;
6 | import org.junit.jupiter.api.Assertions;
7 | import org.junit.jupiter.api.Test;
8 | import org.mockito.Mockito;
9 |
10 | import java.time.LocalDate;
11 |
12 |
13 | class ApplicationCheckerTest {
14 | @Test
15 | void all_ok() {
16 |
17 | ScholarshipRepositoryImpl fakeRepository = Mockito.mock(ScholarshipRepositoryImpl.class);
18 | Mockito.when(fakeRepository.find(55688L)).thenReturn(new Scholarship(LocalDate.MAX));
19 |
20 |
21 | LocalDate expected = LocalDate.of(2029, 12, 31);
22 | Mockito.mockStatic(LocalDate.class).when(LocalDate::now).thenReturn(expected);
23 |
24 |
25 | ApplicationChecker service = new ApplicationChecker(fakeRepository);
26 |
27 | ApplicationForm applicationForm = new ApplicationForm(777L, 55688L);
28 |
29 | boolean actual = service.checkTime(applicationForm);
30 |
31 | Assertions.assertTrue(actual);
32 |
33 |
34 | }
35 | }
--------------------------------------------------------------------------------
/src/test/java/idv/kuma/itehlp2021/scholarship/command/usecase/ApplyScholarshipServiceCodeFirstTest.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.usecase;
2 |
3 | import idv.kuma.itehlp2021.scholarship.command.adapter.ApplicationForm;
4 | import idv.kuma.itehlp2021.scholarship.command.adapter.ClientSideErrorException;
5 | import idv.kuma.itehlp2021.scholarship.command.adapter.ServerSideErrorException;
6 | import org.junit.jupiter.api.Test;
7 |
8 | import static org.mockito.Mockito.*;
9 |
10 | class ApplyScholarshipServiceCodeFirstTest {
11 |
12 | @Test
13 | void check_ok_then_create() throws ServerSideErrorException, ClientSideErrorException {
14 |
15 | // 準備申請表
16 | ApplicationForm application = new ApplicationForm(777L, 55688L);
17 |
18 | // 準備假 checker
19 | ApplicationChecker checker = mock(ApplicationChecker.class);
20 | when(checker.checkTime(application)).thenReturn(true);
21 |
22 | // 準備假 repository
23 | ApplicationRepository repository = mock(ApplicationRepository.class);
24 |
25 | // 執行
26 | new ApplyScholarshipServiceCodeFirst(checker, repository).apply(application);
27 |
28 | // 驗證:真的有 create 一次
29 | verify(repository, times(1)).create(application);
30 | }
31 |
32 | @Test
33 | void check_NOT_ok_then_DONT_create() throws ServerSideErrorException, ClientSideErrorException {
34 |
35 | // 準備申請表
36 | ApplicationForm applicationForm = new ApplicationForm(777L, 55688L);
37 |
38 | // 準備假 checker
39 | ApplicationChecker checker = mock(ApplicationChecker.class);
40 | when(checker.checkTime(applicationForm)).thenReturn(false);
41 |
42 | // 準備假 repository
43 | ApplicationRepository repository = mock(ApplicationRepository.class);
44 |
45 | // 執行
46 | new ApplyScholarshipServiceCodeFirst(checker, repository).apply(applicationForm);
47 |
48 | // 驗證:真的完全有 create 過
49 | verify(repository, never()).create(applicationForm);
50 | }
51 | }
--------------------------------------------------------------------------------
/src/test/java/idv/kuma/itehlp2021/scholarship/command/usecase/ApplyScholarshipServiceTest.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.command.usecase;
2 |
3 | import idv.kuma.itehlp2021.scholarship.command.adapter.ApplicationForm;
4 | import idv.kuma.itehlp2021.scholarship.command.adapter.ClientSideErrorException;
5 | import idv.kuma.itehlp2021.scholarship.command.adapter.ServerSideErrorException;
6 | import idv.kuma.itehlp2021.scholarship.command.entity.Application;
7 | import idv.kuma.itehlp2021.scholarship.command.entity.Scholarship;
8 | import idv.kuma.itehlp2021.student.Student;
9 | import idv.kuma.itehlp2021.student.register.StudentRepository;
10 | import org.junit.jupiter.api.Assertions;
11 | import org.junit.jupiter.api.BeforeAll;
12 | import org.junit.jupiter.api.BeforeEach;
13 | import org.junit.jupiter.api.Test;
14 | import org.mockito.MockedStatic;
15 | import org.mockito.Mockito;
16 |
17 | import java.time.LocalDate;
18 | import java.util.Optional;
19 |
20 | import static org.mockito.ArgumentMatchers.any;
21 |
22 |
23 | class ApplyScholarshipServiceTest {
24 |
25 |
26 | private static final LocalDate july31 = LocalDate.of(2021, 7, 31);
27 | private static final LocalDate august01 = LocalDate.of(2021, 8, 1);
28 |
29 | private static MockedStatic mocked;
30 |
31 | private StudentRepository studentRepository;
32 | private ApplyScholarshipService applyScholarshipService;
33 | private ClientSideErrorException clientSideException;
34 | private ServerSideErrorException serverSideErrorException;
35 | private ScholarshipRepository scholarshipRepository;
36 | private ApplicationRepository applicationRepository;
37 |
38 | @BeforeAll
39 | static void beforeAll() {
40 | mocked = Mockito.mockStatic(LocalDate.class);
41 | }
42 |
43 | @BeforeEach
44 | void setUp() {
45 | studentRepository = Mockito.mock(StudentRepository.class);
46 | scholarshipRepository = Mockito.mock(ScholarshipRepository.class);
47 | applicationRepository = Mockito.mock(ApplicationRepository.class);
48 | applyScholarshipService = new ApplyScholarshipService(studentRepository, scholarshipRepository, applicationRepository);
49 | }
50 |
51 | @Test
52 | void all_ok() throws ServerSideErrorException, ClientSideErrorException, RepositoryAccessDataFailException {
53 |
54 | given_student_exists(12345L, "PhD");
55 |
56 | given_scholarship_exists(98765L, scholarship());
57 |
58 | given_today_is(july31);
59 |
60 | when_apply_with_form_then_NO_error(application_form(12345L, 98765L));
61 |
62 |
63 | }
64 |
65 | private void given_student_exists(long studentId, String program) throws RepositoryAccessDataFailException {
66 | Mockito.when(studentRepository.find(studentId))
67 | .thenReturn(Optional.of(new Student("Michael", "Jordan", program)));
68 | }
69 |
70 | private void given_scholarship_exists(long scholarshipId, Scholarship scholarship) throws RepositoryAccessDataFailException {
71 | Mockito.when(scholarshipRepository.findOptional(scholarshipId))
72 | .thenReturn(Optional.of(scholarship));
73 | }
74 |
75 | private Scholarship scholarship() {
76 | return new Scholarship(LocalDate.MAX);
77 | }
78 |
79 | private void given_today_is(LocalDate today) {
80 |
81 | mocked.when(LocalDate::now).thenReturn(today);
82 |
83 | }
84 |
85 | private void when_apply_with_form_then_NO_error(ApplicationForm form) throws ClientSideErrorException, ServerSideErrorException {
86 |
87 | applyScholarshipService.apply(form);
88 | }
89 |
90 | private ApplicationForm application_form(long studentId, long scholarshipId) {
91 | return new ApplicationForm(studentId, scholarshipId);
92 | }
93 |
94 | @Test
95 | void when_student_not_exist_then_987() throws RepositoryAccessDataFailException {
96 |
97 |
98 | given_student_NOT_exists(12345L);
99 | given_scholarship_exists(98765L, scholarship());
100 |
101 | when_apply_with_form_and_client_side_error_happens(application_form(12345L, 98765L));
102 |
103 | then_client_side_error_code_is(987);
104 |
105 | }
106 |
107 | private void given_student_NOT_exists(long studentId) throws RepositoryAccessDataFailException {
108 | Mockito.when(studentRepository.find(studentId))
109 | .thenReturn(Optional.empty());
110 | }
111 |
112 | private void when_apply_with_form_and_client_side_error_happens(ApplicationForm applicationForm) {
113 |
114 | this.clientSideException = Assertions.assertThrows(ClientSideErrorException.class,
115 | () -> applyScholarshipService.apply(applicationForm));
116 | }
117 |
118 | private void then_client_side_error_code_is(int code) {
119 | Assertions.assertEquals(code, clientSideException.getCode());
120 | }
121 |
122 | @Test
123 | void when_DB_fail_on_getting_student_then_666() throws RepositoryAccessDataFailException {
124 |
125 | given_scholarship_exists(98765L, scholarship());
126 | assume_repository_would_fail_on_getting_student(12345L);
127 |
128 | when_apply_and_fail_on_server_side(application_form(12345L, 98765L));
129 |
130 | then_server_side_error_code_should_be(666);
131 |
132 | }
133 |
134 | private void assume_repository_would_fail_on_getting_student(long studentId) throws RepositoryAccessDataFailException {
135 | Mockito.when(studentRepository.find(studentId))
136 | .thenThrow(new RepositoryAccessDataFailException());
137 | }
138 |
139 | private void when_apply_and_fail_on_server_side(ApplicationForm applicationForm) {
140 |
141 | serverSideErrorException = Assertions.assertThrows(ServerSideErrorException.class,
142 | () -> applyScholarshipService.apply(applicationForm));
143 | }
144 |
145 | private void then_server_side_error_code_should_be(int code) {
146 | Assertions.assertEquals(code, serverSideErrorException.getCode());
147 | }
148 |
149 | @Test
150 | void when_scholarship_not_exist_then_369() throws RepositoryAccessDataFailException {
151 |
152 | given_student_exists(12345L);
153 | given_scholarship_NOT_exists(98765L);
154 |
155 | when_apply_with_form_and_client_side_error_happens(application_form(12345L, 98765L));
156 |
157 | then_client_side_error_code_is(369);
158 |
159 | }
160 |
161 | private void given_student_exists(long studentId) throws RepositoryAccessDataFailException {
162 | Mockito.when(studentRepository.find(studentId))
163 | .thenReturn(Optional.of(new Student("Michael", "Jordan")));
164 | }
165 |
166 | private void given_scholarship_NOT_exists(long scholarshipId) throws RepositoryAccessDataFailException {
167 | Mockito.when(scholarshipRepository.findOptional(scholarshipId))
168 | .thenReturn(Optional.empty());
169 | }
170 |
171 | @Test
172 | void when_DB_fail_on_getting_scholarship_then_666() throws RepositoryAccessDataFailException {
173 |
174 | given_student_exists(12345L);
175 |
176 | assume_DB_would_fail_on_getting_scholarship_data(98765L);
177 |
178 | when_apply_and_fail_on_server_side(application_form(12345L, 98765L));
179 |
180 | then_server_side_error_code_should_be(666);
181 |
182 | }
183 |
184 | private void assume_DB_would_fail_on_getting_scholarship_data(long scholarshipId) throws RepositoryAccessDataFailException {
185 | Mockito.when(scholarshipRepository.findOptional(scholarshipId))
186 | .thenThrow(new RepositoryAccessDataFailException());
187 | }
188 |
189 | @Test
190 | void when_overtime_then_374() throws RepositoryAccessDataFailException {
191 |
192 | given_student_exists(12345L);
193 | given_scholarship_exists(98765L, scholarship(july31));
194 | given_today_is(august01);
195 |
196 | when_apply_with_form_and_client_side_error_happens(application_form(12345L, 98765L));
197 |
198 | then_client_side_error_code_is(374);
199 |
200 | }
201 |
202 | private Scholarship scholarship(LocalDate deadline) {
203 | return new Scholarship(deadline);
204 | }
205 |
206 | @Test
207 | void when_disqualified_then_375() throws RepositoryAccessDataFailException {
208 |
209 | given_student_exists(12345L);
210 |
211 | given_scholarship_exists(98765L, scholarship(july31));
212 |
213 | given_today_is(july31);
214 |
215 | when_apply_with_form_and_client_side_error_happens(application_form(12345L, 98765L));
216 |
217 | then_client_side_error_code_is(375);
218 |
219 | }
220 |
221 | @Test
222 | void when_DB_fail_on_writing_application_to_DB_then_666() throws RepositoryAccessDataFailException {
223 |
224 | given_student_exists(12345L, "PhD");
225 |
226 | given_scholarship_exists(98765L, scholarship());
227 |
228 | given_today_is(july31);
229 |
230 | assume_DB_would_fail_on_creating_application_data();
231 |
232 | when_apply_and_fail_on_server_side(application_form(12345L, 98765L));
233 |
234 | then_server_side_error_code_should_be(666);
235 |
236 | }
237 |
238 | private void assume_DB_would_fail_on_creating_application_data() throws RepositoryAccessDataFailException {
239 | Mockito.doThrow(new RepositoryAccessDataFailException())
240 | .when(applicationRepository).create(any(Application.class));
241 | }
242 |
243 |
244 | }
245 |
--------------------------------------------------------------------------------
/src/test/java/idv/kuma/itehlp2021/scholarship/find_and_notify/FindTopAndNotifyServiceTest.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.find_and_notify;
2 |
3 | import idv.kuma.itehlp2021.scholarship.Transcript;
4 | import idv.kuma.itehlp2021.scholarship.notify.SendResultEmailService;
5 | import org.junit.jupiter.api.Test;
6 | import org.mockito.Mockito;
7 |
8 | import java.util.Arrays;
9 |
10 | import static org.mockito.ArgumentMatchers.anyLong;
11 | import static org.mockito.ArgumentMatchers.eq;
12 |
13 | class FindTopAndNotifyServiceTest {
14 |
15 | private final TranscriptRepository repository = Mockito.mock(TranscriptRepository.class);
16 | private final SendResultEmailService emailService = Mockito.mock(SendResultEmailService.class);
17 | private final FindTopAndNotifyService service = new FindTopAndNotifyService(repository, emailService);
18 |
19 | @Test
20 | void one_student() {
21 |
22 | given_highest_score_students("2021-fall", 9527L, transcript(55688L));
23 |
24 | when_execute_service("2021-fall", 9527L);
25 |
26 | then_send_email_like(55688L, 1);
27 |
28 | }
29 |
30 | @Test
31 | void many_students() {
32 |
33 | given_highest_score_students("2021-fall", 9527L,
34 | transcript(55688L), transcript(3345678L));
35 |
36 | when_execute_service("2021-fall", 9527L);
37 |
38 | then_send_email_like(55688L, 1);
39 | then_send_email_like(3345678L, 1);
40 |
41 | }
42 |
43 | @Test
44 | void NO_students() {
45 |
46 | given_highest_score_students("2021-fall", 9527L);
47 |
48 | when_execute_service("2021-fall", 9527L);
49 |
50 | then_NEVER_send_emails();
51 |
52 |
53 | }
54 |
55 | private void then_NEVER_send_emails() {
56 | Mockito.verify(emailService, Mockito.times(0))
57 | .send(anyLong(), eq("Congratulations! You've got Scholarship"));
58 | }
59 |
60 |
61 | private void then_send_email_like(long studentId, int invokes) {
62 | Mockito.verify(emailService, Mockito.times(invokes))
63 | .send(studentId, "Congratulations! You've got Scholarship");
64 | }
65 |
66 | private void when_execute_service(String semester, long courseId) {
67 | service.execute(semester, courseId);
68 | }
69 |
70 | private Transcript transcript(long studentId) {
71 | return new Transcript(studentId);
72 | }
73 |
74 | private void given_highest_score_students(String semester, long courseId, Transcript... transcripts) {
75 | Mockito.when(repository.findHighestScore(semester, courseId))
76 | .thenReturn(Arrays.asList(
77 | transcripts
78 | ));
79 | }
80 | }
--------------------------------------------------------------------------------
/src/test/java/idv/kuma/itehlp2021/scholarship/notify/SendResultEmailServiceSilentTest.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.notify;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.mockito.ArgumentCaptor;
5 | import org.mockito.Mockito;
6 |
7 | import java.util.Arrays;
8 | import java.util.List;
9 | import java.util.concurrent.ExecutorService;
10 |
11 | import static org.mockito.ArgumentMatchers.any;
12 |
13 | class SendResultEmailServiceSilentTest {
14 | @Test
15 | void silent_send() {
16 |
17 | Mailer mailer = Mockito.mock(Mailer.class);
18 | ExecutorService es = Mockito.mock(ExecutorService.class);
19 |
20 | SendResultEmailService service = new SendResultEmailService(mailer, es);
21 |
22 | service.silentSend(
23 | Arrays.asList(
24 | new ScholarshipResult(),
25 | new ScholarshipResult(),
26 | new ScholarshipResult()
27 | )
28 | );
29 |
30 | ArgumentCaptor captor = ArgumentCaptor.forClass(Runnable.class);
31 | Mockito.verify(es, Mockito.times(3)).submit(captor.capture());
32 |
33 | List runnables = captor.getAllValues();
34 |
35 | runnables.forEach(
36 | Runnable::run
37 | );
38 |
39 | Mockito.verify(mailer, Mockito.times(3)).silentSend(any(ScholarshipResult.class));
40 |
41 |
42 | }
43 | }
--------------------------------------------------------------------------------
/src/test/java/idv/kuma/itehlp2021/scholarship/notify/SendResultEmailServiceTest.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.scholarship.notify;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.mockito.Mockito;
5 | import org.mockito.stubbing.OngoingStubbing;
6 |
7 | import java.util.ArrayList;
8 | import java.util.Arrays;
9 | import java.util.List;
10 | import java.util.concurrent.ExecutionException;
11 | import java.util.concurrent.Future;
12 |
13 | import static org.junit.jupiter.api.Assertions.assertEquals;
14 | import static org.mockito.ArgumentMatchers.any;
15 | import static org.mockito.Mockito.when;
16 |
17 | class SendResultEmailServiceTest {
18 |
19 | private final Mailer mailer = Mockito.mock(Mailer.class);
20 | private List> futures;
21 | private SendResultEmailService service = new SendResultEmailService(mailer);
22 |
23 | @Test
24 | void when_send_returns_future() throws ExecutionException, InterruptedException {
25 |
26 | // 準備假 Mailer
27 | Mailer mailer = Mockito.mock(Mailer.class);
28 | SendResultEmailService service = new SendResultEmailService(mailer);
29 |
30 | // 假 Mailer 會回傳兩個 true,一個 false
31 | when(mailer.send(any(ScholarshipResult.class)))
32 | .thenReturn(true, true, false);
33 |
34 | // 跑起來
35 | List> futures = service.send(
36 | Arrays.asList(
37 | new ScholarshipResult(),
38 | new ScholarshipResult(),
39 | new ScholarshipResult()
40 | ));
41 |
42 | // 檢查 Future 裡 true 與 false 的個數
43 | int goods = 0;
44 | int bads = 0;
45 | for (Future future : futures) {
46 | if (future.get()) {
47 | goods++;
48 | } else {
49 | bads++;
50 | }
51 | }
52 | assertEquals(2, goods);
53 | assertEquals(1, bads);
54 |
55 | }
56 |
57 |
58 | @Test
59 | void when_send_returns_future_refactor_the_test() throws ExecutionException, InterruptedException {
60 |
61 | // 假 Mailer 會回傳兩個 true,一個 false
62 | assume_mailer_execution_result_would_be(true, true, false);
63 |
64 | // 跑起來
65 | when_send_with_results(3);
66 |
67 | // 檢查 Future 裡 true 與 false 的個數
68 | then_counts_in_futures_will_be(true, 2);
69 | then_counts_in_futures_will_be(false, 1);
70 |
71 | }
72 |
73 | private void then_counts_in_futures_will_be(boolean target, int expected) throws InterruptedException, ExecutionException {
74 | assertEquals(expected, count_in_futures(target));
75 | }
76 |
77 | private void assume_mailer_execution_result_would_be(boolean first, boolean... followings) {
78 | OngoingStubbing stubbing =
79 | when(mailer.send(any(ScholarshipResult.class))).thenReturn(first);
80 |
81 | for (boolean expected : followings) {
82 | stubbing.thenReturn(expected);
83 | }
84 | }
85 |
86 | private void when_send_with_results(int number) {
87 | futures = this.service.send(results(number));
88 | }
89 |
90 | private List results(int number) {
91 |
92 | List results = new ArrayList<>();
93 | for (int i = 0; i < number; i++) {
94 | results.add(new ScholarshipResult());
95 | }
96 | return results;
97 | }
98 |
99 | private int count_in_futures(boolean target) throws InterruptedException, ExecutionException {
100 | int counts = 0;
101 | for (Future future : futures) {
102 | if (future.get() == target) {
103 | counts++;
104 | }
105 | }
106 | return counts;
107 | }
108 | }
--------------------------------------------------------------------------------
/src/test/java/idv/kuma/itehlp2021/student/StudentTest.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.student;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import static org.junit.jupiter.api.Assertions.assertEquals;
6 |
7 | class StudentTest {
8 | @Test
9 | void full_name() {
10 |
11 | Student student = new Student("Michael", "Jordan");
12 |
13 | String actual = student.getFullName();
14 |
15 | assertEquals("Michael Jordan", actual);
16 | }
17 | }
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/test/java/idv/kuma/itehlp2021/student/register/RegisterControllerTest.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.student.register;
2 |
3 | import com.fasterxml.jackson.core.JsonProcessingException;
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import org.junit.jupiter.api.Test;
6 | import org.mockito.Mockito;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
9 | import org.springframework.boot.test.context.SpringBootTest;
10 | import org.springframework.boot.test.mock.mockito.MockBean;
11 | import org.springframework.http.HttpStatus;
12 | import org.springframework.http.MediaType;
13 | import org.springframework.test.web.servlet.MockMvc;
14 | import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
15 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
16 |
17 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
18 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
19 |
20 | @SpringBootTest
21 | @AutoConfigureMockMvc
22 | class RegisterControllerTest {
23 |
24 | private final ObjectMapper objectMapper = new ObjectMapper();
25 |
26 | @Autowired
27 | private MockMvc mockMvc;
28 |
29 | @MockBean
30 | private RegisterService service;
31 |
32 |
33 | @Test
34 | void all_ok() throws Exception {
35 | mockMvc.perform(request("/register", 35L))
36 | .andExpect(status().is(HttpStatus.OK.value()));
37 | }
38 |
39 | private MockHttpServletRequestBuilder request(String url, long studentId) throws JsonProcessingException {
40 | return MockMvcRequestBuilders
41 | .post(url)
42 | .contentType(MediaType.APPLICATION_JSON)
43 | .content(objectMapper.writeValueAsString(new RegisterRequest(studentId)));
44 | }
45 |
46 | @Test
47 | void student_not_found() throws Exception {
48 | given_student_not_exists(35L);
49 |
50 | mockMvc.perform(request("/register", 35L))
51 | .andExpect(status().is(HttpStatus.BAD_REQUEST.value()))
52 | .andExpect(content().json(bad_response(987)));
53 | }
54 |
55 | private String bad_response(int errorCode) throws JsonProcessingException {
56 | return objectMapper.writeValueAsString(ApiResponse.bad(errorCode));
57 | }
58 |
59 | private void given_student_not_exists(long studentId) throws StudentNotExistException {
60 | Mockito.doThrow(new StudentNotExistException("ANY_MESSAGE"))
61 | .when(service)
62 | .execute(new RegisterRequest(studentId));
63 | }
64 |
65 |
66 | }
--------------------------------------------------------------------------------
/src/test/java/idv/kuma/itehlp2021/student/register/RegisterServiceTest.java:
--------------------------------------------------------------------------------
1 | package idv.kuma.itehlp2021.student.register;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.Test;
5 | import org.mockito.Mockito;
6 |
7 | import static org.assertj.core.api.Assertions.assertThat;
8 | import static org.assertj.core.api.Fail.fail;
9 |
10 | class RegisterServiceTest {
11 |
12 | private final StudentRepositoryImpl repository = Mockito.mock(StudentRepositoryImpl.class);
13 |
14 | @Test
15 | void when_student_not_exists() throws DataNotFoundException {
16 |
17 | given_student_NOT_exists(35L);
18 |
19 | try {
20 | create_register_service().execute(request(35L));
21 | fail("should throw exception");
22 | } catch (StudentNotExistException e) {
23 | assertThat(e).hasMessageContaining("not exists");
24 | }
25 |
26 |
27 | }
28 |
29 | private void given_student_NOT_exists(long studentId) throws DataNotFoundException {
30 | Mockito.doThrow(new DataNotFoundException())
31 | .when(repository).register(request(studentId));
32 | }
33 |
34 | private RegisterService create_register_service() {
35 | return new RegisterService(repository);
36 | }
37 |
38 | private RegisterRequest request(long studentId) {
39 | return new RegisterRequest(studentId);
40 | }
41 |
42 | @Test
43 | void when_student_not_exists_alternative() throws DataNotFoundException {
44 |
45 | given_student_NOT_exists(35L);
46 |
47 | StudentNotExistException actualException = Assertions.assertThrows(
48 | StudentNotExistException.class,
49 | () -> create_register_service().execute(request(35L))
50 | );
51 | assertThat(actualException).hasMessageContaining("not exists");
52 |
53 |
54 | }
55 | }
--------------------------------------------------------------------------------