Compare commits

..

No commits in common. "master" and "final" have entirely different histories.

100 changed files with 2339 additions and 1336 deletions

3
.gitignore vendored
View file

@ -39,6 +39,3 @@ build/
shortener_db_schema.sql shortener_db_schema.sql
/out/ /out/
/logs/ /logs/
logs/
/test-output/
test-output/

36
.m2/settings.xml Normal file
View file

@ -0,0 +1,36 @@
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<activeProfiles>
<activeProfile>github</activeProfile>
</activeProfiles>
<profiles>
<profile>
<id>github</id>
<repositories>
<repository>
<id>central</id>
<url>https://repo1.maven.org/maven2</url>
</repository>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/aamitn/URLShortener</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</profile>
</profiles>
<servers>
<server>
<id>github</id>
<username>aamitn</username>
<password>#TOKEN#</password>
</server>
</servers>
</settings>

View file

@ -1,44 +1,22 @@
#syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
# Add the following lines to tag the image (replace 'your_username' and 'shortener-app' with your Docker Hub username and repository name) # Add the following lines to tag the image (replace 'your_username' and 'shortener-app' with your Docker Hub username and repository name)
ARG VERSION=latest ARG VERSION=latest
ARG IMAGE_NAME=nmpl/shortener ARG IMAGE_NAME=bigwiz/shortener
ARG TAG=$VERSION ARG TAG=$VERSION
# Stage 1: Build the application
#
#------STAGE 1: Build the application-----#
#
# Get Maven with JDK 21
FROM maven:3.9.6-eclipse-temurin-21 AS builder FROM maven:3.9.6-eclipse-temurin-21 AS builder
#Cloud Install : Clone the repository # Clone the repository
#RUN git clone https://github.com/aamitn/URLShortener.git RUN git clone https://github.com/aamitn/URLShortener.git
RUN mkdir -p URLShortener
#Local Install
ADD . /URLShortener
# Change working directory to the repo directory
WORKDIR /URLShortener WORKDIR /URLShortener
# Docker makes db accessible like this : mysql://<container-name>:port instead of mysql://<server-ip>:port
# Example real world db access url : mysql://127.0.0.1:3306
# Example Docker db access url : mysql://database:3306 (container name is datbase)
# Change the database ip in app config to the database docker container name/service
RUN sed -i "s|database.ip=127.0.0.1|database.ip=database|g" src/main/resources/application.properties
# Build the application # Build the application
RUN mvn clean install RUN mvn clean install
# Stage 2: Create the final image
#
#------STAGE 2: Deploy the Generated War-----#
#
## Get Tomacat 10 with JDK21
FROM tomcat:10-jdk21-openjdk-slim FROM tomcat:10-jdk21-openjdk-slim
# Set environment variables # Set environment variables
@ -46,19 +24,29 @@ ENV CATALINA_BASE /usr/local/tomcat
ENV CATALINA_HOME /usr/local/tomcat ENV CATALINA_HOME /usr/local/tomcat
ENV PATH $CATALINA_HOME/bin:$PATH ENV PATH $CATALINA_HOME/bin:$PATH
# Copy the WAR file from the builder stage # Copy the WAR file from the builder stage
COPY --from=builder /URLShortener/target/shortener.war $CATALINA_BASE/webapps/ COPY --from=builder /URLShortener/target/shortener.war $CATALINA_BASE/webapps/
#
#------STAGE 3: Configure and Start Application Server-----#
#
# Add configuration for document base path # Add configuration for document base path
COPY server.xml $CATALINA_BASE/conf/server.xml COPY server.xml $CATALINA_BASE/conf/server.xml
# Expose ports # Expose ports
EXPOSE 8080 EXPOSE 8080
EXPOSE 3306
# Copy the startup script
COPY shortener.sh /usr/local/tomcat/shortener.sh
# Copy the sql file
COPY create.sql /usr/local/tomcat/create.sql
# Grant execute permissions to the startup.sh script
RUN chmod +x /usr/local/tomcat/shortener.sh
# Start Tomcat and MariaDB using the startup script # Start Tomcat and MariaDB using the startup script
CMD ["catalina.sh", "run"] CMD ["sh", "/usr/local/tomcat/shortener.sh"]

201
LICENSE
View file

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

162
README.md
View file

@ -17,12 +17,10 @@ An enterprise-grade, powerful and scalable URL shortener with integrated bio pag
* [Prerequisites](#prerequisites) * [Prerequisites](#prerequisites)
* [Local Setup](#local-setup) * [Local Setup](#local-setup)
* [Deployment](#deployment) * [Deployment](#deployment)
* [Cloud Native Deployment](#cloud-native-deployment) * [Usage](#usage)
* [SMS Configuration](#sms-service-configuration)
* [Use Cases](#use-cases)
* [Contributing](#contributing) * [Contributing](#contributing)
* [License](#license) * [License](#license)
***
## Overview ## Overview
This project is an enterprise-grade URL shortener and bio page application developed with Spring Boot. It provides a robust solution for shortening URLs and creating bio pages for users. The application is designed for scalability and includes features such as analytics, user management, and subscription plans. This project is an enterprise-grade URL shortener and bio page application developed with Spring Boot. It provides a robust solution for shortening URLs and creating bio pages for users. The application is designed for scalability and includes features such as analytics, user management, and subscription plans.
@ -56,8 +54,6 @@ This project is an enterprise-grade URL shortener and bio page application devel
* Tailwind CSS: A utility-first CSS framework that makes it easy to design and build modern, responsive user interfaces. * Tailwind CSS: A utility-first CSS framework that makes it easy to design and build modern, responsive user interfaces.
* Flowbite: A design system and UI toolkit for building responsive web applications. * Flowbite: A design system and UI toolkit for building responsive web applications.
***
## Getting Started ## Getting Started
### Prerequisites ### Prerequisites
@ -70,7 +66,7 @@ Before you begin, ensure you have the following installed:
* Your favorite IDE (IntelliJ, Eclipse, etc.) * Your favorite IDE (IntelliJ, Eclipse, etc.)
### 1-Click Local Installation <a href="https://github.com/aamitn/URLShortener/releases/download/final/install.cmd">Windows</a> | <a href="https://github.com/aamitn/URLShortener/releases/download/final/install.sh">Linux</a> ### 1-Click Local Installation <a href="https://github.com/aamitn/URLShortener/releases/download/installer%2Bwar/install.bat">Windows</a> | <a href="https://github.com/aamitn/URLShortener/releases/download/installer%2Bwar/install.sh">Linux</a>
### Windows ### Windows
1. Open a Command Prompt with administrator privileges. 1. Open a Command Prompt with administrator privileges.
@ -87,9 +83,6 @@ Before you begin, ensure you have the following installed:
./install.sh ./install.sh
3. Follow the on-screen instructions. 3. Follow the on-screen instructions.
##### Finally, Select Application Server : Tomcat/Wildfly
***
### Local Setup ### Local Setup
@ -97,32 +90,30 @@ Before you begin, ensure you have the following installed:
```plaintext ```plaintext
git clone https://github.com/aamitn/URLShortener.git git clone https://github.com/aamitn/URLShortener.git
cd URLShortener
``` ```
2. **Apply the Database Schema:** 2. **Apply the Database Schema:**
Execute the provided `create.sql` file to set up the necessary tables and schema for the application. This script will also create the required database. Execute the provided **db\_schema.sql** file to set up the necessary tables and schema for the application. This script will also create the required database.
```plaintext ```plaintext
mysql -u your_username -p{your_password} < create.sql mysql -u your_username -p < path/to/db_schema.sql
``` ```
3. Update the application.properties file: 3. Update the application.properties file:
* Update the `application.properties` file in the src/main/resources directory with your application configuration. Update the application.properties file in the src/main/resources directory with your database configuration.
4. Build and run the application: 4. Build and run the application:
```plaintext ```plaintext
mvn spring-boot:run cd URLShortener
mvn spring-boot:run
``` ```
5. Access the application at http://localhost:8080 5. Access the application at http://localhost:8080
*** ### Deployment
### Deployment Setup
1. **Build a deployable WAR:** 1. **Build a deployable WAR:**
@ -146,9 +137,9 @@ Before you begin, ensure you have the following installed:
* Access the Tomcat Manager web interface at `http://localhost:8080/manager/html` (replace with your Tomcat server address). * Access the Tomcat Manager web interface at `http://localhost:8080/manager/html` (replace with your Tomcat server address).
* Log in with your Tomcat manager credentials. * Log in with your Tomcat manager credentials.
* Navigate to the "WAR file to deploy" section. * Navigate to the "WAR file to deploy" section.
* Choose the `shortener.war` file using the file upload button. * Choose the `shorten.war` file using the file upload button.
* Click the "Deploy" button. * Click the "Deploy" button.
3. **Configure Variables on deployed war:** 3. **Configure environment variables:**
Set environment variables for cloud-specific settings. Set environment variables for cloud-specific settings.
@ -176,9 +167,9 @@ Before you begin, ensure you have the following installed:
6. **Adjust Configuration (Optional):** If you need to customize the application configuration after deployment, you can find the **application.properties** file within the deployed WAR file. THe file can be accesses as : 6. **Adjust Configuration (Optional):** If you need to customize the application configuration after deployment, you can find the **application.properties** file within the deployed WAR file. THe file can be accesses as :
```plaintext ```plaintext
vi /path/to/tomcat/webapps/shortener/WEB-INF/classes/application.properties vi /path/to/tomcat/webapps/your-app/WEB-INF/classes/application.properties
``` ```
***
### Cloud Native Deployment ### Cloud Native Deployment
@ -188,9 +179,6 @@ To deploy the URL Shortener application on your cloud environment, follow the st
* [Docker](https://www.docker.com/get-started) * [Docker](https://www.docker.com/get-started)
* [Docker Compose](https://docs.docker.com/compose/install/) * [Docker Compose](https://docs.docker.com/compose/install/)
* [Kubernetes/K8s](https://kubernetes.io/docs/setup/)
* [Minikube](https://minikube.sigs.k8s.io/docs/start/)
* [Helm](https://helm.sh/docs/intro/install/)
1. **Clone the Repository:** 1. **Clone the Repository:**
@ -198,33 +186,30 @@ To deploy the URL Shortener application on your cloud environment, follow the st
git clone https://github.com/your-username/URLShortener.git git clone https://github.com/your-username/URLShortener.git
cd URLShortener cd URLShortener
``` ```
2. ***Deploy Using Docker***
* Build and Run Your Own Image: 2. **Build and Run Your Own Image:**
```plaintext ```plaintext
docker build -t shortener:latest . docker build -t shortener:latest .
docker run -p 8080:8080 -p 3306:3306 shortener:latest docker run -p 8080:8080 -p 3306:3306 shortener:latest
``` ```
<p style="text-align: center;"> OR </p>
* Use Our Pre-built Image with Docker Compose: 3. **Use Our Pre-built Image with Docker Compose:**
```plaintext ```plaintext
docker compose build docker compose build
docker compose up docker compose up
``` ```
* Customizing Docker Compose Configuration: 4. **Customizing Docker Compose Configuration:**
In the `docker-compose.yml` file, you can customize the build source for the Shortener service: In the **docker-compose.yml** file, you can customize the build source for the Shortener service:
```plaintext ```plaintext
# Build from docker hub image .Comment/Uncomment Below #Build from docker hub image .Comment/Uncomment Below
image: nmpl/shortener:latest image: nmpl/shortener:latest
# Build from local Dockerfile.Comment/Uncomment Below #Build from local Dockerfile.Comment/Uncomment Below
# build: # build:
# context: . # context: .
# dockerfile: Dockerfilekerfile # dockerfile: Dockerfilekerfile
@ -233,115 +218,45 @@ To deploy the URL Shortener application on your cloud environment, follow the st
Comment or uncomment the relevant lines based on whether you want to use the pre-built image from Docker Hub or build from the local Dockerfile. Comment or uncomment the relevant lines based on whether you want to use the pre-built image from Docker Hub or build from the local Dockerfile.
3. ***Deploy Using K8s*** 5. **Deploy Using K8s**
* Setup Kubernetes Deployment and Service: * **Setup Kubernetes Deployment and Service:**
```plaintext ```plaintext
cd k8s kubectl apply -f shortener-deployment.yaml
kubectl apply -f deploy.yaml,service.yaml,data.yaml kubectl apply -f shortener-service.yaml
``` ```
* Expose the K8s service: * **Use the following command to monitor the deployment:**
```plaintext
kubectl port-forward service/shortener-app 8080:8080
```
Access Application at http://localhost:8080
<center>OR</center>
If you're using minikube, run the below command to run the app:
```plaintext
minikube service shortener-app
```
* Use the following command to monitor the deployment:
```plaintext
kubectl get pods
```
4. ***Deploy Using Helm Chart***
* Create Deployment from helm chart named "instance" , with a service named "instance-shortener-chart" :
```plaintext
cd k8s
helm install instance shortener-chart/ --values shortener-chart/values.yaml
```
* Expose the K8s service:
```plaintext
kubectl port-forward service/instance-shortener-chart 8080:8080
```
<center>OR</center>
If you're using minikube, run the below command to run the app:**
```plaintext
minikube service shortener-app
```
* Use the following command to monitor the deployment:
```plaintext ```plaintext
kubectl get pods kubectl get pods
``` ```
Wait until the pod is in the "Running" state. Wait until the pod is in the "Running" state.
* Access the Application * **Access the Application**
Depending on your Kubernetes setup, you might need to get the external IP of the service: Depending on your Kubernetes setup, you might need to get the external IP of the service:
```plaintext ```plaintext
kubectl get service instance-shortener-chart kubectl get service shortener-service
``` ```
Access your application using the provided external IP(from command output) or at : http://localhost:8080. Access your application using the provided external IP.
***
### SMS Service Configuration
To configure the SMS service, you need to specify parameters related to the SMS provider in the `application.properties` file.
* #### Managed SMS Provider (Uses HttpSms API from https://httpsms.com/)
* Generate API Key : https://httpsms.com/settings/
* HttpSms API Docs : https://api.httpsms.com/
```properties
sms.provider=managed
managed.sms.api.key=your_managed_sms_api_key
managed.sms.phone-number=123456789
* #### Self-hosted SMS Provider (Host our open source android-based SMS web gateway : https://api.httpsms.com/)
```properties
sms.provider=selfhosted
selfhosted.gateway.url=https://your-smsgateway-url/index.php
selfhosted.device.id=your_device_id
selfhosted.hash=your_device_hash
***
### App Health ### App Health
Check application status from the /monitoring page at : http://localhost:8080/monitoring
### Docs
Check application OpenAPI 3.0 compliant docs at: http://localhost:8080/docs-ui
*** * Check application status from the /monitoring page example http://localhost:8080/monitoring
### Use Cases ### Usage Cases
1. Shorten URLs by visiting the URL Shortener page. 1. Shorten URLs by visiting the URL Shortener page.
2. Access analytics for each shortened URL. 2. Access analytics for each shortened URL.
3. Create and manage bio pages in the Bio section. 3. Create and manage bio pages in the Bio section.
***
### Contributing ### Contributing
Contributions are welcome! Please follow the contribution guidelines. Contributions are welcome! Please follow the contribution guidelines.
@ -349,6 +264,3 @@ Contributions are welcome! Please follow the contribution guidelines.
### License ### License
This project is licensed under the MIT License - see the LICENSE.md file for details. This project is licensed under the MIT License - see the LICENSE.md file for details.
***
<p style="text-align: center;">&copy; Bitmutex Technologies | 2024</p>

View file

@ -1,43 +1,37 @@
version: '3.8' version: '3.8'
volumes:
data:
services: services:
database: shortener-app:
image: mariadb
environment:
MYSQL_ROOT_PASSWORD: 1234qwer
MYSQL_DATABASE: shortener
MYSQL_USER: shortener_user
MYSQL_PASSWORD: 1234qwer
volumes:
- data:/var/lib/mysql
- ./create.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "3306:3306"
app:
labels: labels:
- "TUSC The URL Shortener Company" - "TUSC The URL Shortener Company"
#Build from docker hub image .Comment/Uncomment Below #Build from docker hub image .Comment/Uncomment Below
image: nmpl/shortener:latest image: nmpl/shortener:latest
# Build from local Dockerfile.Comment/Uncomment Below #Build from local Dockerfile.Comment/Uncomment Below
# build: # build:
# context: . # context: .
# dockerfile: Dockerfile # dockerfile: Dockerfile
healthcheck:
test: [ "CMD-SHELL", "curl", "-f", "http://localhost:8080/monitoring" ]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
restart: unless-stopped
ports: ports:
- "8080:8080" - "8080:8080"
depends_on: - "3306:3306"
- database
volumes:
- shortener-db-data:/var/lib/mysql
- type: volume
source: shortener-db-data
target: /var/lib/mysql
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:8080/monitoring" ]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
restart: unless-stopped
volumes:
shortener-db-data:
name: shortener-db-data

BIN
image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

102
installers/install.bat Normal file
View file

@ -0,0 +1,102 @@
@echo off
setlocal enabledelayedexpansion
REM Set Tomcat and URL variables
set "TOMCAT_VERSION=10.1.9"
set "TOMCAT_URL=https://archive.apache.org/dist/tomcat/tomcat-10/v%TOMCAT_VERSION%/bin/apache-tomcat-%TOMCAT_VERSION%-windows-x64.zip"
set "WAR_URL=https://github.com/aamitn/URLShortener/releases/download/WAR/shortener.war"
set "SQL_FILE_URL=https://github.com/aamitn/URLShortener/raw/master/create.sql"
set "SERVER_XML_URL=https://raw.githubusercontent.com/aamitn/URLShortener/master/server.xml"
REM Function to download and extract Tomcat
:download_and_extract_tomcat
echo Downloading Tomcat...
curl -O "%TOMCAT_URL%"
PowerShell Expand-Archive "apache-tomcat-%TOMCAT_VERSION%-windows-x64.zip" -DestinationPath .
del "apache-tomcat-%TOMCAT_VERSION%-windows-x64.zip"
REM Function to download the WAR file
:download_war_file
echo Downloading shortener.war...
curl -LJO "%WAR_URL%"
REM Function to configure Tomcat and deploy the WAR file
:configure_and_deploy
echo Configuring and deploying...
copy shortener.war "apache-tomcat-%TOMCAT_VERSION%\webapps\"
REM Downloading the server.xml file
echo Downloading server.xml...
curl -LJO "%SERVER_XML_URL%"
REM Replace the existing server.xml with the downloaded one
echo Replacing server.xml...
copy /Y server.xml "apache-tomcat-%TOMCAT_VERSION%\conf\server.xml"
REM Run Tomcat server using startup.bat
:run_tomcat
echo Running Tomcat server...
cd "apache-tomcat-%TOMCAT_VERSION%\bin"
call startup.bat
REM Wait for Tomcat to start (adjust sleep time as needed)
timeout /t 20 /nobreak
call shutdown.bat
timeout /t 2 /nobreak
call startup.bat
cd ..
cd ..
REM Main Script Execution
if "%OS%"=="Windows_NT" (
call :download_and_install_mariadb
) else (
echo Unsupported operating system.
exit /b 1
)
echo Local deployment completed successfully!
exit /b 0
REM Function to download and install MariaDB
:download_and_install_mariadb
echo Downloading MariaDB installer...
curl -O "https://mirror.docker.ru/mariadb//mariadb-11.4.0/winx64-packages/mariadb-11.4.0-winx64.zip"
echo Installing MariaDB...
PowerShell Expand-Archive "mariadb-11.4.0-winx64.zip" -DestinationPath .
del "mariadb-11.4.0-winx64.zip"
cd mariadb-11.4.0-winx64/bin
cd..
cd..
@echo SET PASSWORD FOR 'root'@'localhost' = PASSWORD('1234qwer');> dbinit.txt
REM Get the current directory
set "CURRENT_DIR=%CD%"
REM Create db.bat script for database initialization
echo timeout /t 1 /nobreak > db.bat
echo cd mariadb-11.4.0-winx64/bin >> db.bat
echo mysql -u root -p1234qwer -e "CREATE DATABASE IF NOT EXISTS shortener;" >> db.bat
echo curl -LJO "https://github.com/aamitn/URLShortener/raw/master/create.sql" >> db.bat
echo mysql -u root -p1234qwer ^< create.sql >> db.bat
echo mysql -u root -p1234qwer -e "SHOW DATABASES;" >> db.bat
echo mysql -u root -p1234qwer -e "USE shortener" >> db.bat
echo mysql -u root -p1234qwer -e "SELECT * FROM shortener;" >> db.bat
echo del create.sql >> db.bat
echo echo Deployed Successfully... >> db.bat
echo start "" http://localhost:8080 >> db.bat
echo exit /b 0 >> db.bat
REM Start the db.bat script
start db.bat
REM Navigate to mariadb-11.4.0-winx64/bin
cd mariadb-11.4.0-winx64/bin
REM Initialize DB
call mariadb-install-db.exe
REM Run MariaDB server with the init file
call mysqld.exe --console --init-file="%CURRENT_DIR%\\dbinit.txt"

View file

@ -1,155 +0,0 @@
@echo off
REM Set Tomcat and URL variables
set "TOMCAT_VERSION=10.1.9"
set "TOMCAT_URL=https://archive.apache.org/dist/tomcat/tomcat-10/v%TOMCAT_VERSION%/bin/apache-tomcat-%TOMCAT_VERSION%-windows-x64.zip"
set "WAR_URL=https://github.com/aamitn/URLShortener/releases/download/final/shortener.war"
set "SQL_FILE_URL=https://github.com/aamitn/URLShortener/raw/master/create.sql"
set "SERVER_XML_URL=https://raw.githubusercontent.com/aamitn/URLShortener/master/server.xml"
REM Set WildFly and URL variables
set "WILDFLY_VERSION=31.0.0.Final"
set "WILDFLY_URL=https://github.com/wildfly/wildfly/releases/download/%WILDFLY_VERSION%/wildfly-%WILDFLY_VERSION%.zip"
set "WAR_URL=https://github.com/aamitn/URLShortener/releases/download/final/shortener.war"
setlocal enabledelayedexpansion
REM Ask the user for deployment target
echo Choose the deployment target:
echo 1. Tomcat
echo 2. WildFly
set /p DEPLOYMENT_TARGET=Enter the number (default is Tomcat):
REM Set default deployment target to Tomcat if user input is empty
if not defined DEPLOYMENT_TARGET set "DEPLOYMENT_TARGET=1"
REM Function to download the WAR file
:download_war_file
echo Downloading shortener.war...
curl -LJO "%WAR_URL%"
REM Function to deploy the WAR file
:deploy_war
echo Deploying shortener.war to %DEPLOYMENT_TARGET%...
if "%DEPLOYMENT_TARGET%"=="1" (
setlocal enabledelayedexpansion
REM Function to download and extract Tomcat
:download_and_extract_tomcat
echo Downloading Tomcat...
curl -O "%TOMCAT_URL%"
PowerShell Expand-Archive "apache-tomcat-%TOMCAT_VERSION%-windows-x64.zip" -DestinationPath .
del "apache-tomcat-%TOMCAT_VERSION%-windows-x64.zip"
REM Function to configure Tomcat and deploy the WAR file
:configure_and_deploy
echo Configuring and deploying...
copy shortener.war "apache-tomcat-%TOMCAT_VERSION%\webapps\"
REM Downloading the server.xml file
echo Downloading server.xml...
curl -LJO "%SERVER_XML_URL%"
REM Replace the existing server.xml with the downloaded one
echo Replacing server.xml...
copy /Y server.xml "apache-tomcat-%TOMCAT_VERSION%\conf\server.xml"
REM Run Tomcat server using startup.bat
:run_tomcat
echo Running Tomcat server...
cd "apache-tomcat-%TOMCAT_VERSION%\bin"
call startup.bat
REM Wait for Tomcat to start (adjust sleep time as needed)
timeout /t 30 /nobreak
call shutdown.bat
timeout /t 2 /nobreak
call startup.bat
cd ..
cd ..
) else if "%DEPLOYMENT_TARGET%"=="2" (
@echo off
setlocal enabledelayedexpansion
REM Function to download and extract WildFly
:download_and_extract_wildfly
echo Downloading WildFly...
curl -LJO "%WILDFLY_URL%"
PowerShell Expand-Archive "wildfly-%WILDFLY_VERSION%.zip" -DestinationPath .
del "wildfly-%WILDFLY_VERSION%.zip"
REM Function to deploy the WAR file
:deploy_war
echo Deploying shortener.war to WildFly...
copy shortener.war "wildfly-%WILDFLY_VERSION%\standalone\deployments\"
REM Run WildFly server
:run_wildfly
echo Running WildFly...
cd "wildfly-%WILDFLY_VERSION%\bin"
dir
start standalone.bat -c standalone-full.xml
echo WildFly deployment completed successfully!
cd..
cd..
timeout /t 3 /nobreak
) else (
echo Invalid deployment target selected.
exit /b 1
)
echo Deployment Stage 1 Done
REM Function to download and install MariaDB
REM Check if MariaDB is already installed
if not exist mariadb-11.4.0-winx64 (
echo Downloading MariaDB installer...
curl -O "https://mirrors.aliyun.com/mariadb//mariadb-11.4.0/winx64-packages/mariadb-11.4.0-winx64.zip"
echo Installing MariaDB...
PowerShell Expand-Archive "mariadb-11.4.0-winx64.zip" -DestinationPath .
del "mariadb-11.4.0-winx64.zip"
)
cd mariadb-11.4.0-winx64/bin
@echo SET PASSWORD FOR 'root'@'localhost' = PASSWORD('1234qwer');> dbinit.txt
cd..
cd..
REM Navigate to mariadb-11.4.0-winx64/bin
cd mariadb-11.4.0-winx64/bin
REM Get the current directory
set "CURRENT_DIR=%CD%"
REM Initialize DB
call mariadb-install-db.exe
REM Run MariaDB server with the init file
start mysqld.exe --console --init-file="%CURRENT_DIR%\\dbinit.txt"
REM Create DB
timeout /t 1 /nobreak
mysql -u root -p1234qwer -e "CREATE DATABASE IF NOT EXISTS shortener;"
curl -LJO "https://github.com/aamitn/URLShortener/raw/master/create.sql"
mysql -u root -p1234qwer < create.sql
mysql -u root -p1234qwer -e "SHOW ENGINE PERFORMANCE_SCHEMA STATUS;SHOW ENGINE INNODB STATUS;"
mysql -u root -p1234qwer -e "SHOW DATABASES;"
mysql -u root -p1234qwer -e "USE shortener; SHOW TABLES; SHOW TABLE STATUS\G;"
del create.sql
REM start browser
echo Application Deployed Successfully...
start "" http://localhost:8080
timeout /t 20 /nobreak
exit /b

View file

@ -3,7 +3,7 @@
# Set Tomcat and URL variables # Set Tomcat and URL variables
TOMCAT_VERSION="10.0.0-M15" TOMCAT_VERSION="10.0.0-M15"
TOMCAT_URL="https://archive.apache.org/dist/tomcat/tomcat-10/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz" TOMCAT_URL="https://archive.apache.org/dist/tomcat/tomcat-10/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz"
WAR_URL="https://github.com/aamitn/URLShortener/releases/download/final/shortener.war" WAR_URL="https://github.com/aamitn/URLShortener/releases/download/WAR/shortener.war"
SERVER_XML_CONTEXT="<Context path=\"\" docBase=\"shortener\" debug=\"0\" reloadable=\"true\"></Context>" SERVER_XML_CONTEXT="<Context path=\"\" docBase=\"shortener\" debug=\"0\" reloadable=\"true\"></Context>"
SQL_FILE_URL="https://github.com/aamitn/URLShortener/raw/master/create.sql" SQL_FILE_URL="https://github.com/aamitn/URLShortener/raw/master/create.sql"

View file

@ -1,12 +0,0 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
labels:
io.kompose.service: data
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Mi

View file

@ -1,42 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
name.full: "TUSC-The Url Shortener Company"
labels:
io.kompose.service: shortener-app
name: shortener-app
spec:
replicas: 1
selector:
matchLabels:
io.kompose.service: shortener-app
strategy:
type: Recreate
template:
metadata:
annotations:
name.full: "TUSC-The Url Shortener Company"
labels:
io.kompose.network/k8s-default: "true"
io.kompose.service: shortener-app
spec:
containers:
- image: nmpl/shortener:k8s
name: shortener-app
ports:
- containerPort: 8080
hostPort: 8080
protocol: TCP
- containerPort: 3306
hostPort: 3306
protocol: TCP
volumeMounts:
- mountPath: /var/lib/mysql
name: data
restartPolicy: Always
volumes:
- name: data
persistentVolumeClaim:
claimName: data

View file

@ -1,51 +0,0 @@
# syntax=docker/dockerfile:1
# Add the following lines to tag the image (replace 'your_username' and 'shortener-app' with your Docker Hub username and repository name)
ARG VERSION=k8s
ARG IMAGE_NAME=bigwiz/shortener
ARG TAG=$VERSION
# Stage 1: Build the application
FROM maven:3.9.6-eclipse-temurin-21 AS builder
# Clone the repository
RUN git clone https://github.com/aamitn/URLShortener.git
WORKDIR /URLShortener
# Build the application
RUN mvn clean install
# Stage 2: Create the final image
FROM tomcat:10-jdk21-openjdk-slim
# Set environment variables
ENV CATALINA_BASE /usr/local/tomcat
ENV CATALINA_HOME /usr/local/tomcat
ENV PATH $CATALINA_HOME/bin:$PATH
# Copy the WAR file from the builder stage
COPY --from=builder /URLShortener/target/shortener.war $CATALINA_BASE/webapps/
# Add configuration for document base path
COPY --from=builder /URLShortener/server.xml $CATALINA_BASE/conf/server.xml
# Expose ports
EXPOSE 8080
EXPOSE 3306
# Copy the startup script
COPY shortener.sh /usr/local/tomcat/shortener.sh
# Copy the sql file
COPY --from=builder /URLShortener/create.sql /usr/local/tomcat/create.sql
# Grant execute permissions to the startup.sh script
RUN chmod +x /usr/local/tomcat/shortener.sh
# Start Tomcat and MariaDB using the startup script
CMD ["sh", "/usr/local/tomcat/shortener.sh"]

View file

@ -1,22 +0,0 @@
version: '3.8'
services:
shortener-app:
labels:
- "TUSC"
#Build from docker hub image .Comment/Uncomment Below
image: nmpl/shortener:k8s
#Build from local Dockerfile.Comment/Uncomment Below
#build:
#dockerfile: Dockerfile
ports:
- "8080:8080"
- "3306:3306"
volumes:
- data:/var/lib/mysql
restart: unless-stopped
volumes:
data:

View file

@ -1,17 +0,0 @@
apiVersion: v1
kind: Service
metadata:
annotations:
name.full: "TUSC-The Url Shortener Company"
labels:
io.kompose.service: shortener-app
name: shortener-app
spec:
ports:
- name: "8080"
port: 8080
targetPort: 8080
selector:
io.kompose.service: shortener-app

Binary file not shown.

View file

@ -1,23 +0,0 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View file

@ -1,24 +0,0 @@
apiVersion: v2
name: shortener-chart
description: A Helm chart for Kubernetes
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.16.0"

View file

@ -1,22 +0,0 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "shortener-chart.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "shortener-chart.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "shortener-chart.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "shortener-chart.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}

View file

@ -1,62 +0,0 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "shortener-chart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "shortener-chart.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "shortener-chart.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "shortener-chart.labels" -}}
helm.sh/chart: {{ include "shortener-chart.chart" . }}
{{ include "shortener-chart.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "shortener-chart.selectorLabels" -}}
app.kubernetes.io/name: {{ include "shortener-chart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "shortener-chart.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "shortener-chart.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View file

@ -1,68 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "shortener-chart.fullname" . }}
labels:
{{- include "shortener-chart.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "shortener-chart.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "shortener-chart.labels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "shortener-chart.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.readinessProbe | nindent 12 }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.volumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.volumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View file

@ -1,32 +0,0 @@
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "shortener-chart.fullname" . }}
labels:
{{- include "shortener-chart.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "shortener-chart.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

View file

@ -1,61 +0,0 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "shortener-chart.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "shortener-chart.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
pathType: {{ .pathType }}
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}
port:
number: {{ $svcPort }}
{{- else }}
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

View file

@ -1,15 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "shortener-chart.fullname" . }}
labels:
{{- include "shortener-chart.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "shortener-chart.selectorLabels" . | nindent 4 }}

View file

@ -1,13 +0,0 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "shortener-chart.serviceAccountName" . }}
labels:
{{- include "shortener-chart.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
automountServiceAccountToken: {{ .Values.serviceAccount.automount }}
{{- end }}

View file

@ -1,15 +0,0 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "shortener-chart.fullname" . }}-test-connection"
labels:
{{- include "shortener-chart.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "shortener-chart.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

View file

@ -1,107 +0,0 @@
# Default values for shortener-chart.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: nmpl/shortener
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: "k8s"
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# Automatically mount a ServiceAccount's API credentials?
automount: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
podAnnotations: {}
podLabels: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
service:
type: ClusterIP
port: 8080
ingress:
enabled: false
className: ""
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
# Additional volumes on the output Deployment definition.
volumes: []
# - name: foo
# secret:
# secretName: mysecret
# optional: false
# Additional volumeMounts on the output Deployment definition.
volumeMounts: []
# - name: foo
# mountPath: "/etc/foo"
# readOnly: true
nodeSelector: {}
tolerations: []
affinity: {}

25
pom.xml
View file

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.2</version> <version>3.2.1</version>
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
<groupId>com.bitmutex</groupId> <groupId>com.bitmutex</groupId>
@ -86,7 +86,7 @@
<dependency> <dependency>
<groupId>org.jetbrains</groupId> <groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId> <artifactId>annotations</artifactId>
<version>24.1.0</version> <version>RELEASE</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
@ -180,8 +180,11 @@
<build> <build>
<finalName>${project.artifactId}</finalName> <finalName>${project.artifactId}</finalName>
<pluginManagement>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
@ -197,7 +200,22 @@
</property> </property>
</properties> </properties>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-maven-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>integration-test</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@ -227,8 +245,9 @@
</executions> </executions>
</plugin> </plugin>
</plugins>
</plugins>
</pluginManagement>
</build> </build>

21
shortener-deployment.yaml Normal file
View file

@ -0,0 +1,21 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: shortener-deployment
labels:
app: shortener
spec:
replicas: 3 # Adjust as needed
selector:
matchLabels:
app: shortener
template:
metadata:
labels:
app: shortener
spec:
containers:
- name: shortener
image: nmpl/shortener:latest
ports:
- containerPort: 8080

12
shortener-service.yaml Normal file
View file

@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: shortener-service
spec:
selector:
app: shortener
ports:
- protocol: TCP
port: 8080
targetPort: 8080
type: LoadBalancer

View file

@ -12,7 +12,7 @@ service mariadb start
echo "Waiting for MariaDB to start (adjust sleep time as needed)..." echo "Waiting for MariaDB to start (adjust sleep time as needed)..."
# Wait for MariaDB to start # Wait for MariaDB to start
while ! mysqladmin ping -hlocalhost -uroot --silent; do while ! mysqladmin ping -hlocalhost -uroot -p'YOUR_PASSWORD' --silent; do
echo "MariaDB is not yet available. Waiting..." echo "MariaDB is not yet available. Waiting..."
sleep 5 sleep 5
done done
@ -23,8 +23,7 @@ mysql -u root -e "source /usr/local/tomcat/create.sql"
echo "Displaying databases and tables..." echo "Displaying databases and tables..."
# Display the databases and tables # Display the databases and tables
mysql -u root -e "SHOW ENGINE PERFORMANCE_SCHEMA STATUS;SHOW ENGINE INNODB STATUS;" mysql -u root -e "SHOW DATABASES; USE shortener; SHOW TABLES;"
mysql -u root -e "SHOW DATABASES; USE shortener; SHOW TABLES; SHOW TABLE STATUS\G;"
echo "Altering user and reloading privileges..." echo "Altering user and reloading privileges..."
# Run SQL commands to alter user and reload privileges # Run SQL commands to alter user and reload privileges
@ -35,4 +34,4 @@ EOF
echo "Starting Tomcat in the background..." echo "Starting Tomcat in the background..."
# Start Tomcat in the background # Start Tomcat in the background
sh /usr/local/tomcat/bin/catalina.sh run sh /usr/local/tomcat/bin/catalina.sh run

View file

@ -5,88 +5,41 @@ import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.net.URI; import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient; import java.net.http.HttpClient;
import java.net.http.HttpRequest; import java.net.http.HttpRequest;
import java.net.http.HttpResponse; import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
@Service @Service
public class SmsService { public class SmsService {
private final SmsServiceConfig smsServiceConfig;
private static final Logger logger = LoggerFactory.getLogger(SmsService.class); private static final Logger logger = LoggerFactory.getLogger(SmsService.class);
public SmsService(SmsServiceConfig smsServiceConfig) {
this.smsServiceConfig = smsServiceConfig;
}
public void sendSms(String toNumber, String message) { public void sendSms(String toNumber, String message) {
try { try {
if ("managed".equals(smsServiceConfig.getSmsProvider())) { // Send the SMS to the user (you need to implement this part)
sendSmsToManagedProvider(toNumber, message); var client = HttpClient.newHttpClient();
} else if ("selfhosted".equals(smsServiceConfig.getSmsProvider())) { var apiKey = "Vucf1nCa4ed-AMNGv6CnsycfQT28yLUA8NEvY7IZ87-Piv855UBcjfo29Zb8XPZt";
sendSmsToSelfHostedGateway(toNumber, message);
} else { String payload = "{\n" +
logger.error("Invalid SMS provider configuration"); " \"content\": \"" + message + "\",\n" + // Use concatenation for variables
} " \"from\": \"+9038556097\",\n" +
" \"to\": \"" + toNumber + "\"\n" +
"}";
var requestBuild = HttpRequest.newBuilder()
.uri(URI.create("https://api.httpsms.com/v1/messages/send"))
.header("accept", "application/json")
.header("Content-Type", "application/json")
.header("x-api-key", apiKey)
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
var response = client.send(requestBuild, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
logger.info("SMS sent successfully to {}", toNumber); logger.info("SMS sent successfully to {}", toNumber);
logger.debug("SMS API response: {}", response.body());
} catch (Exception e) { } catch (Exception e) {
// Log and handle SMS sending failure // Log and handle SMS sending failure
logger.error("Failed to send SMS to {}: {}", toNumber, e.getMessage(), e); logger.error("Failed to send SMS to {}: {}", toNumber, e.getMessage(), e);
} }
} }
}
private void sendSmsToManagedProvider(String toNumber, String message) throws Exception {
var client = HttpClient.newHttpClient();
var apiKey = smsServiceConfig.getManagedSmsApiKey();
var phoneNumber = smsServiceConfig.getManagedPhoneNumber();
String payload = "{\n" +
" \"content\": \"" + message + "\",\n" +
" \"from\": \"+"+ phoneNumber + "\",\n" +
" \"to\": \"" + toNumber + "\"\n" +
"}";
var requestBuild = HttpRequest.newBuilder()
.uri(URI.create("https://api.httpsms.com/v1/messages/send"))
.header("accept", "application/json")
.header("Content-Type", "application/json")
.header("x-api-key", apiKey)
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
var response = client.send(requestBuild, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
logger.info("External SMS API response: {}", response.body());
}
private void sendSmsToSelfHostedGateway(String toNumber, String message) throws Exception {
// Replace with your actual SMSGateway server URL, device ID, and hash
String gatewayUrl = smsServiceConfig.getSelfHostedGatewayUrl();
String deviceId = smsServiceConfig.getSelfHostedDeviceId();
String hash = smsServiceConfig.getSelfHostedHash();
// Encode special characters in the message
String encodedMessage = URLEncoder.encode(message, StandardCharsets.UTF_8.toString());
// Build the final URL
var finalUrl = String.format("%s?id=%s&h=%s&to=%s&message=%s", gatewayUrl, deviceId, hash, toNumber, encodedMessage);
var client = HttpClient.newHttpClient();
var requestBuild = HttpRequest.newBuilder()
.uri(URI.create(finalUrl))
.header("accept", "application/json")
.header("Content-Type", "application/json")
.GET()
.build();
var response = client.send(requestBuild, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
logger.info("SMSGateway API response: {}", response.body());
}
}

View file

@ -1,50 +0,0 @@
package com.bitmutex.shortener;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SmsServiceConfig {
@Value("${sms.provider}")
private String smsProvider;
@Value("${managed.sms.api.key}")
private String managedSmsApiKey;
@Value("${managed.sms.phone-number}")
private String managedPhoneNumber;
@Value("${selfhosted.gateway.url}")
private String selfHostedGatewayUrl;
@Value("${selfhosted.device.id}")
private String selfHostedDeviceId;
@Value("${selfhosted.hash}")
private String selfHostedHash;
public String getSmsProvider() {
return smsProvider;
}
public String getManagedPhoneNumber() {
return managedPhoneNumber;
}
public String getManagedSmsApiKey() {
return managedSmsApiKey;
}
public String getSelfHostedGatewayUrl() {
return selfHostedGatewayUrl;
}
public String getSelfHostedDeviceId() {
return selfHostedDeviceId;
}
public String getSelfHostedHash() {
return selfHostedHash;
}
}

View file

@ -1,18 +1,3 @@
/**
* @file gateway.php
* @brief Url Shortener Application
* Github Repository : https://github.com/aamitn/URLShortener
* JAVA 21 / Spring Boot 3
*
* @author Amit Kumar Nandi (Bitmutex Technologies) <amit@bitmutex.com>
* @version 1.0.5
* @date 2023-03-30
* @since 2022-09-10
* @copyright (c) 2023-2024 Bitmutex Technologies
* @copyright GNU Lesser General Public License
*
*********************************************************************/
package com.bitmutex.shortener; package com.bitmutex.shortener;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;

View file

@ -91,20 +91,9 @@ springdoc.api-docs.path=/docs
springdoc.swagger-ui.path=/docs-ui springdoc.swagger-ui.path=/docs-ui
springdoc.swagger-ui.operationsSorter=method springdoc.swagger-ui.operationsSorter=method
springdoc.show-actuator=true springdoc.show-actuator=true
#Springdoc Swagger Config
springdoc.swagger-ui.oauthClientId=Iv1.8d3d0ea51b7e7da3 springdoc.swagger-ui.oauthClientId=Iv1.8d3d0ea51b7e7da3
springdoc.swagger-ui.oauthClientSecret=2176086761d073b2082afdc4af0207fa7d1d274b springdoc.swagger-ui.oauthClientSecret=2176086761d073b2082afdc4af0207fa7d1d274b
springdoc.swagger-ui.oauthAppName=Bitmutex Shortener springdoc.swagger-ui.oauthAppName=Bitmutex Shortener
springdoc.swagger-ui.oauthScopeSeparator=/v springdoc.swagger-ui.oauthScopeSeparator=/v
springdoc.swagger-ui.showRequestHeaders=true springdoc.swagger-ui.showRequestHeaders=true
# SMS Provider Configuration (managed/selfhosted)
sms.provider=selfhosted
# For Managed SMS Provider (uses httpSms API from https://httpsms.com/settings)
managed.sms.api.key=Vucf1nCa4ed-AMNGv6CnsycfQT28yLUA8NEvY7IZ87-Piv855UBcjfo29Zb8XPZt
managed.sms.phone-number=9038556097
# For Self-Hosted SMSGateway (android app url : https://bitmutexsms.000webhostapp.com/index.php?id=f0edf9a81c2461b5&h=b4132c)
selfhosted.gateway.url=https://bitmutexsms.000webhostapp.com/index.php
selfhosted.device.id=f0edf9a81c2461b5
selfhosted.hash=b4132c

View file

@ -1,43 +1,22 @@
<!-- logback-spring.xml --> <!-- logback-spring.xml -->
<configuration> <configuration>
<!-- File Appender -->
<appender name="file" class="ch.qos.logback.core.FileAppender">
<file>logs/shortener.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- Define the base path for log files --> <!-- Appender for console output -->
<property name="LOG_PATH" value="logs" />
<!-- Console Appender -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder> </encoder>
</appender> </appender>
<!-- Rolling File Appender --> <!-- Root logger -->
<appender name="rollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/shortener.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/shortener.log.%d{yyyy-MM-dd}.%i.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>4</maxIndex>
<!-- keep 30 days' worth of history -->
<maxHistory>30</maxHistory>
<maxFileSize>5MB</maxFileSize>
<totalSizeCap>1GB</totalSizeCap>
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
</appender>
<!-- Async Appender (Wrap appenders in Async to make logging asynchronous) -->
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="rollingFile" />
<appender-ref ref="console" />
</appender>
<!-- Root Logger -->
<root level="debug"> <root level="debug">
<appender-ref ref="async" /> <appender-ref ref="file"/>
</root> </root>
</configuration>
</configuration>

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
<deployment>
<!-- Exclude the JBoss Logger subsystem -->
<exclude-subsystems>
<subsystem name="logging" />
</exclude-subsystems>
</deployment>
</jboss-deployment-structure>

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<context-root>/</context-root>
<virtual-host>default-host</virtual-host> <!-- does mapping to host inside server -->
</jboss-web>

View file

@ -55,6 +55,7 @@ public class ForgotPasswordControllerTest {
assert "forgot-password".equals(result); assert "forgot-password".equals(result);
verify(userService, times(1)).findByEmail(email);
verify(userService, never()).save(any(UserEntity.class)); verify(userService, never()).save(any(UserEntity.class));
verify(javaMailSender, never()).send((MimeMessage) any()); verify(javaMailSender, never()).send((MimeMessage) any());
} }

View file

@ -53,6 +53,7 @@ public class ForgotUsernameControllerTest {
assertEquals("forgot-username", result); assertEquals("forgot-username", result);
verify(userService, times(1)).findByEmail(email);
verify(javaMailSender, never()).send((SimpleMailMessage) any()); verify(javaMailSender, never()).send((SimpleMailMessage) any());
} }

View file

@ -8,6 +8,8 @@ import org.springframework.http.ResponseEntity;
import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
@ -46,4 +48,56 @@ public class SubscriptionControllerTest {
assert responseEntity.getStatusCode() == HttpStatus.OK; assert responseEntity.getStatusCode() == HttpStatus.OK;
} }
@Test
public void testChangeSubscription_InternalServerError() {
// Arrange
String username = "testUser";
String newPlanName = "newPlan";
when(userService.findByUsername(username)).thenReturn(Optional.of(new UserEntity()));
doThrow(new RuntimeException("Simulated error")).when(subscriptionService).changeSubscription(any(), any());
// Act
ResponseEntity<?> responseEntity = subscriptionController.changeSubscription(username, newPlanName);
// Assert
assert responseEntity.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR;
assert responseEntity.getBody().equals("Error changing subscription: Simulated error");
}
@Test
public void testGetSubscriptionDetails_Success() {
// Arrange
String username = "testUser";
UserEntity userEntity = new UserEntity();
Map<String, Object> subscriptionDetails = new HashMap<>();
when(userService.findByUsername(username)).thenReturn(Optional.of(userEntity));
when(subscriptionService.getCurrentSubscriptionDetails(userEntity)).thenReturn(subscriptionDetails);
// Act
ResponseEntity<Object> responseEntity = subscriptionController.getSubscriptionDetails(username);
// Assert
verify(subscriptionService).getCurrentSubscriptionDetails(any(UserEntity.class));
assert responseEntity.getStatusCode() == HttpStatus.OK;
assert responseEntity.getBody() == subscriptionDetails;
}
@Test
public void testGetSubscriptionDetails_InternalServerError() {
// Arrange
String username = "testUser";
when(userService.findByUsername(username)).thenReturn(Optional.of(new UserEntity()));
doThrow(new RuntimeException("Simulated error")).when(subscriptionService).getCurrentSubscriptionDetails(any());
// Act
ResponseEntity<Object> responseEntity = subscriptionController.getSubscriptionDetails(username);
// Assert
assert responseEntity.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR;
assert responseEntity.getBody().equals("Internal Server Error");
}
} }

View file

@ -11,10 +11,13 @@ import org.testng.annotations.Test;
import java.util.Optional; import java.util.Optional;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
public class VerificationControllerTest { public class VerificationControllerTest {
@Mock
private UserService userService;
@Mock @Mock
private UserRepository userRepository; private UserRepository userRepository;
@ -56,6 +59,14 @@ public class VerificationControllerTest {
when(otpService.getOtp(email)).thenReturn(Optional.of(otp)); when(otpService.getOtp(email)).thenReturn(Optional.of(otp));
when(userRepository.findByEmail(email)).thenReturn(new UserEntity()); when(userRepository.findByEmail(email)).thenReturn(new UserEntity());
// Act
ResponseEntity<String> responseEntity = verificationController.verifyRegistration(otp, email, model);
// Assert
assert responseEntity.getStatusCode() == HttpStatus.OK;
assert responseEntity.getBody().contains("User verified successfully");
verify(otpService).removeOtpByEmail(email);
verify(userRepository).save(any(UserEntity.class));
} }
@Test @Test

View file

@ -0,0 +1,94 @@
<html>
<head>
<title>TestNG: URLShortener</title>
<link href="../testng.css" rel="stylesheet" type="text/css" />
<link href="../my-testng.css" rel="stylesheet" type="text/css" />
<style type="text/css">
.log { display: none;}
.stack-trace { display: none;}
</style>
<script type="text/javascript">
<!--
function flip(e) {
current = e.style.display;
if (current == 'block') {
e.style.display = 'none';
return 0;
}
else {
e.style.display = 'block';
return 1;
}
}
function toggleBox(szDivId, elem, msg1, msg2)
{
var res = -1; if (document.getElementById) {
res = flip(document.getElementById(szDivId));
}
else if (document.all) {
// this is the way old msie versions work
res = flip(document.all[szDivId]);
}
if(elem) {
if(res == 0) elem.innerHTML = msg1; else elem.innerHTML = msg2;
}
}
function toggleAllBoxes() {
if (document.getElementsByTagName) {
d = document.getElementsByTagName('div');
for (i = 0; i < d.length; i++) {
if (d[i].className == 'log') {
flip(d[i]);
}
}
}
}
// -->
</script>
</head>
<body>
<h2 align='center'>URLShortener</h2><table border='1' align="center">
<tr>
<td>Tests passed/Failed/Skipped:</td><td>3/0/0</td>
</tr><tr>
<td>Started on:</td><td>Thu Jan 18 03:45:08 IST 2024</td>
</tr>
<tr><td>Total time:</td><td>0 seconds (840 ms)</td>
</tr><tr>
<td>Included groups:</td><td></td>
</tr><tr>
<td>Excluded groups:</td><td></td>
</tr>
</table><p/>
<small><i>(Hover the method name to see the test class name)</i></small><p/>
<table width='100%' border='1' class='invocation-passed'>
<tr><td colspan='4' align='center'><b>PASSED TESTS</b></td></tr>
<tr><td><b>Test method</b></td>
<td width="30%"><b>Exception</b></td>
<td width="10%"><b>Time (seconds)</b></td>
<td><b>Instance</b></td>
</tr>
<tr>
<td title='com.bitmutex.shortener.AnalyticsControllerTest.testGetAnalyticsDataWithValidShortUrl()'><b>testGetAnalyticsDataWithValidShortUrl</b><br>Test class: com.bitmutex.shortener.AnalyticsControllerTest</td>
<td></td>
<td>0</td>
<td>com.bitmutex.shortener.AnalyticsControllerTest@7fc2413d</td></tr>
<tr>
<td title='com.bitmutex.shortener.AnalyticsControllerTest.testGetAnalyticsDataWithInvalidShortUrl()'><b>testGetAnalyticsDataWithInvalidShortUrl</b><br>Test class: com.bitmutex.shortener.AnalyticsControllerTest</td>
<td></td>
<td>0</td>
<td>com.bitmutex.shortener.AnalyticsControllerTest@7fc2413d</td></tr>
<tr>
<td title='com.bitmutex.shortener.AnalyticsTest.testAnalyticsEntity()'><b>testAnalyticsEntity</b><br>Test class: com.bitmutex.shortener.AnalyticsTest</td>
<td></td>
<td>0</td>
<td>com.bitmutex.shortener.AnalyticsTest@402e37bc</td></tr>
</table><p>
</body>
</html>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated by org.testng.reporters.JUnitXMLReporter -->
<testsuite ignored="0" hostname="Wiz-Workstation" failures="0" tests="3" name="URLShortener" time="0.84" errors="0" timestamp="2024-01-18T03:45:09 IST">
<testcase classname="com.bitmutex.shortener.AnalyticsTest" name="testAnalyticsEntity" time="0.006"/>
<testcase classname="com.bitmutex.shortener.AnalyticsControllerTest" name="testGetAnalyticsDataWithInvalidShortUrl" time="0.022"/>
<testcase classname="com.bitmutex.shortener.AnalyticsControllerTest" name="testGetAnalyticsDataWithValidShortUrl" time="0.005"/>
</testsuite> <!-- URLShortener -->

View file

@ -0,0 +1,94 @@
<html>
<head>
<title>TestNG: MyTestClass</title>
<link href="../testng.css" rel="stylesheet" type="text/css" />
<link href="../my-testng.css" rel="stylesheet" type="text/css" />
<style type="text/css">
.log { display: none;}
.stack-trace { display: none;}
</style>
<script type="text/javascript">
<!--
function flip(e) {
current = e.style.display;
if (current == 'block') {
e.style.display = 'none';
return 0;
}
else {
e.style.display = 'block';
return 1;
}
}
function toggleBox(szDivId, elem, msg1, msg2)
{
var res = -1; if (document.getElementById) {
res = flip(document.getElementById(szDivId));
}
else if (document.all) {
// this is the way old msie versions work
res = flip(document.all[szDivId]);
}
if(elem) {
if(res == 0) elem.innerHTML = msg1; else elem.innerHTML = msg2;
}
}
function toggleAllBoxes() {
if (document.getElementsByTagName) {
d = document.getElementsByTagName('div');
for (i = 0; i < d.length; i++) {
if (d[i].className == 'log') {
flip(d[i]);
}
}
}
}
// -->
</script>
</head>
<body>
<h2 align='center'>MyTestClass</h2><table border='1' align="center">
<tr>
<td>Tests passed/Failed/Skipped:</td><td>3/0/0</td>
</tr><tr>
<td>Started on:</td><td>Thu Jan 18 04:17:08 IST 2024</td>
</tr>
<tr><td>Total time:</td><td>0 seconds (823 ms)</td>
</tr><tr>
<td>Included groups:</td><td></td>
</tr><tr>
<td>Excluded groups:</td><td></td>
</tr>
</table><p/>
<small><i>(Hover the method name to see the test class name)</i></small><p/>
<table width='100%' border='1' class='invocation-passed'>
<tr><td colspan='4' align='center'><b>PASSED TESTS</b></td></tr>
<tr><td><b>Test method</b></td>
<td width="30%"><b>Exception</b></td>
<td width="10%"><b>Time (seconds)</b></td>
<td><b>Instance</b></td>
</tr>
<tr>
<td title='com.bitmutex.shortener.AnalyticsTest.testAnalyticsEntity()'><b>testAnalyticsEntity</b><br>Test class: com.bitmutex.shortener.AnalyticsTest</td>
<td></td>
<td>0</td>
<td>com.bitmutex.shortener.AnalyticsTest@11a9e7c8</td></tr>
<tr>
<td title='com.bitmutex.shortener.AnalyticsControllerTest.testGetAnalyticsDataWithValidShortUrl()'><b>testGetAnalyticsDataWithValidShortUrl</b><br>Test class: com.bitmutex.shortener.AnalyticsControllerTest</td>
<td></td>
<td>0</td>
<td>com.bitmutex.shortener.AnalyticsControllerTest@233fe9b6</td></tr>
<tr>
<td title='com.bitmutex.shortener.AnalyticsControllerTest.testGetAnalyticsDataWithInvalidShortUrl()'><b>testGetAnalyticsDataWithInvalidShortUrl</b><br>Test class: com.bitmutex.shortener.AnalyticsControllerTest</td>
<td></td>
<td>0</td>
<td>com.bitmutex.shortener.AnalyticsControllerTest@233fe9b6</td></tr>
</table><p>
</body>
</html>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated by org.testng.reporters.JUnitXMLReporter -->
<testsuite ignored="0" hostname="Wiz-Workstation" failures="0" tests="3" name="MyTestClass" time="0.823" errors="0" timestamp="2024-01-18T04:17:09 IST">
<testcase classname="com.bitmutex.shortener.AnalyticsControllerTest" name="testGetAnalyticsDataWithInvalidShortUrl" time="0.016"/>
<testcase classname="com.bitmutex.shortener.AnalyticsControllerTest" name="testGetAnalyticsDataWithValidShortUrl" time="0.004"/>
<testcase classname="com.bitmutex.shortener.AnalyticsTest" name="testAnalyticsEntity" time="0.001"/>
</testsuite> <!-- MyTestClass -->

View file

@ -0,0 +1,94 @@
<html>
<head>
<title>TestNG: UrlShortenerTestSuite</title>
<link href="../testng.css" rel="stylesheet" type="text/css" />
<link href="../my-testng.css" rel="stylesheet" type="text/css" />
<style type="text/css">
.log { display: none;}
.stack-trace { display: none;}
</style>
<script type="text/javascript">
<!--
function flip(e) {
current = e.style.display;
if (current == 'block') {
e.style.display = 'none';
return 0;
}
else {
e.style.display = 'block';
return 1;
}
}
function toggleBox(szDivId, elem, msg1, msg2)
{
var res = -1; if (document.getElementById) {
res = flip(document.getElementById(szDivId));
}
else if (document.all) {
// this is the way old msie versions work
res = flip(document.all[szDivId]);
}
if(elem) {
if(res == 0) elem.innerHTML = msg1; else elem.innerHTML = msg2;
}
}
function toggleAllBoxes() {
if (document.getElementsByTagName) {
d = document.getElementsByTagName('div');
for (i = 0; i < d.length; i++) {
if (d[i].className == 'log') {
flip(d[i]);
}
}
}
}
// -->
</script>
</head>
<body>
<h2 align='center'>UrlShortenerTestSuite</h2><table border='1' align="center">
<tr>
<td>Tests passed/Failed/Skipped:</td><td>3/0/0</td>
</tr><tr>
<td>Started on:</td><td>Thu Jan 18 04:17:28 IST 2024</td>
</tr>
<tr><td>Total time:</td><td>0 seconds (841 ms)</td>
</tr><tr>
<td>Included groups:</td><td></td>
</tr><tr>
<td>Excluded groups:</td><td></td>
</tr>
</table><p/>
<small><i>(Hover the method name to see the test class name)</i></small><p/>
<table width='100%' border='1' class='invocation-passed'>
<tr><td colspan='4' align='center'><b>PASSED TESTS</b></td></tr>
<tr><td><b>Test method</b></td>
<td width="30%"><b>Exception</b></td>
<td width="10%"><b>Time (seconds)</b></td>
<td><b>Instance</b></td>
</tr>
<tr>
<td title='com.bitmutex.shortener.AnalyticsControllerTest.testGetAnalyticsDataWithValidShortUrl()'><b>testGetAnalyticsDataWithValidShortUrl</b><br>Test class: com.bitmutex.shortener.AnalyticsControllerTest</td>
<td></td>
<td>0</td>
<td>com.bitmutex.shortener.AnalyticsControllerTest@402e37bc</td></tr>
<tr>
<td title='com.bitmutex.shortener.AnalyticsTest.testAnalyticsEntity()'><b>testAnalyticsEntity</b><br>Test class: com.bitmutex.shortener.AnalyticsTest</td>
<td></td>
<td>0</td>
<td>com.bitmutex.shortener.AnalyticsTest@7fc2413d</td></tr>
<tr>
<td title='com.bitmutex.shortener.AnalyticsControllerTest.testGetAnalyticsDataWithInvalidShortUrl()'><b>testGetAnalyticsDataWithInvalidShortUrl</b><br>Test class: com.bitmutex.shortener.AnalyticsControllerTest</td>
<td></td>
<td>0</td>
<td>com.bitmutex.shortener.AnalyticsControllerTest@402e37bc</td></tr>
</table><p>
</body>
</html>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated by org.testng.reporters.JUnitXMLReporter -->
<testsuite ignored="0" hostname="Wiz-Workstation" failures="0" tests="3" name="UrlShortenerTestSuite" time="0.841" errors="0" timestamp="2024-01-18T04:17:29 IST">
<testcase classname="com.bitmutex.shortener.AnalyticsControllerTest" name="testGetAnalyticsDataWithInvalidShortUrl" time="0.021"/>
<testcase classname="com.bitmutex.shortener.AnalyticsControllerTest" name="testGetAnalyticsDataWithValidShortUrl" time="0.005"/>
<testcase classname="com.bitmutex.shortener.AnalyticsTest" name="testAnalyticsEntity" time="0.0"/>
</testsuite> <!-- UrlShortenerTestSuite -->

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

BIN
test-output/collapseall.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

View file

@ -0,0 +1,20 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "https://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="https://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>TestNG Report</title>
<style type="text/css">table {margin-bottom:10px;border-collapse:collapse;empty-cells:show}th,td {border:1px solid #009;padding:.25em .5em}th {vertical-align:bottom}td {vertical-align:top}table a {font-weight:bold}.stripe td {background-color: #E6EBF9}.num {text-align:right}.passedodd td {background-color: #3F3}.passedeven td {background-color: #0A0}.skippedodd td {background-color: #DDD}.skippedeven td {background-color: #CCC}.failedodd td,.attn {background-color: #F33}.failedeven td,.stripe .attn {background-color: #D00}.stacktrace {white-space:pre;font-family:monospace}.totop {font-size:85%;text-align:center;border-bottom:2px solid #000}.invisible {display:none}</style>
</head>
<body>
<table>
<tr><th>Test</th><th># Passed</th><th># Skipped</th><th># Retried</th><th># Failed</th><th>Time (ms)</th><th>Included Groups</th><th>Excluded Groups</th></tr>
<tr><th colspan="8">UrlShortenerTestSuite</th></tr>
<tr><td><a href="#t0">UrlShortenerTestSuite</a></td><td class="num">3</td><td class="num">0</td><td class="num">0</td><td class="num">0</td><td class="num">841</td><td></td><td></td></tr>
</table>
<table id='summary'><thead><tr><th>Class</th><th>Method</th><th>Start</th><th>Time (ms)</th></tr></thead><tbody><tr><th colspan="4">UrlShortenerTestSuite</th></tr></tbody><tbody id="t0"><tr><th colspan="4">UrlShortenerTestSuite &#8212; passed</th></tr><tr class="passedeven"><td rowspan="2">com.bitmutex.shortener.AnalyticsControllerTest</td><td><a href="#m0">testGetAnalyticsDataWithInvalidShortUrl</a></td><td rowspan="1">1705531649115</td><td rowspan="1">21</td></tr><tr class="passedeven"><td><a href="#m1">testGetAnalyticsDataWithValidShortUrl</a></td><td rowspan="1">1705531649137</td><td rowspan="1">5</td></tr><tr class="passedodd"><td rowspan="1">com.bitmutex.shortener.AnalyticsTest</td><td><a href="#m2">testAnalyticsEntity</a></td><td rowspan="1">1705531649186</td><td rowspan="1">0</td></tr></tbody>
</table>
<h2>UrlShortenerTestSuite</h2><h3 id="m0">com.bitmutex.shortener.AnalyticsControllerTest#testGetAnalyticsDataWithInvalidShortUrl</h3><table class="result"><tr><th class="invisible"/></tr></table><p class="totop"><a href="#summary">back to summary</a></p>
<h3 id="m1">com.bitmutex.shortener.AnalyticsControllerTest#testGetAnalyticsDataWithValidShortUrl</h3><table class="result"><tr><th class="invisible"/></tr></table><p class="totop"><a href="#summary">back to summary</a></p>
<h3 id="m2">com.bitmutex.shortener.AnalyticsTest#testAnalyticsEntity</h3><table class="result"><tr><th class="invisible"/></tr></table><p class="totop"><a href="#summary">back to summary</a></p>
</body>
</html>

BIN
test-output/failed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 977 B

295
test-output/index.html Normal file
View file

@ -0,0 +1,295 @@
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>TestNG reports</title>
<link type="text/css" href="testng-reports1.css" rel="stylesheet" id="ultra" />
<link type="text/css" href="testng-reports.css" rel="stylesheet" id="retro" disabled="false"/>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="testng-reports.js"></script>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type='text/javascript'>
google.load('visualization', '1', {packages:['table']});
google.setOnLoadCallback(drawTable);
var suiteTableInitFunctions = new Array();
var suiteTableData = new Array();
</script>
<!--
<script type="text/javascript" src="jquery-ui/js/jquery-ui-1.8.16.custom.min.js"></script>
-->
</head>
<body>
<div class="top-banner-root">
<span class="top-banner-title-font">Test results</span>
<button id="button" class="button">Switch Retro Theme</button> <!-- button -->
<br/>
<span class="top-banner-font-1">1 suite</span>
</div> <!-- top-banner-root -->
<div class="navigator-root">
<div class="navigator-suite-header">
<span>All suites</span>
<a href="#" title="Collapse/expand all the suites" class="collapse-all-link">
<img src="collapseall.gif" class="collapse-all-icon">
</img> <!-- collapse-all-icon -->
</a> <!-- collapse-all-link -->
</div> <!-- navigator-suite-header -->
<div class="suite">
<div class="rounded-window">
<div class="suite-header light-rounded-window-top">
<a href="#" panel-name="suite-UrlShortenerTestSuite" class="navigator-link">
<span class="suite-name border-passed">UrlShortenerTestSuite</span>
</a> <!-- navigator-link -->
</div> <!-- suite-header light-rounded-window-top -->
<div class="navigator-suite-content">
<div class="suite-section-title">
<span>Info</span>
</div> <!-- suite-section-title -->
<div class="suite-section-content">
<ul>
<li>
<a href="#" panel-name="test-xml-UrlShortenerTestSuite" class="navigator-link ">
<span>C:\Users\bigwiz\IdeaProjects\URLShortener\src\test\resources\testng.xml</span>
</a> <!-- navigator-link -->
</li>
<li>
<a href="#" panel-name="testlist-UrlShortenerTestSuite" class="navigator-link ">
<span class="test-stats">1 test</span>
</a> <!-- navigator-link -->
</li>
<li>
<a href="#" panel-name="group-UrlShortenerTestSuite" class="navigator-link ">
<span>0 groups</span>
</a> <!-- navigator-link -->
</li>
<li>
<a href="#" panel-name="times-UrlShortenerTestSuite" class="navigator-link ">
<span>Times</span>
</a> <!-- navigator-link -->
</li>
<li>
<a href="#" panel-name="reporter-UrlShortenerTestSuite" class="navigator-link ">
<span>Reporter output</span>
</a> <!-- navigator-link -->
</li>
<li>
<a href="#" panel-name="ignored-methods-UrlShortenerTestSuite" class="navigator-link ">
<span>Ignored methods</span>
</a> <!-- navigator-link -->
</li>
<li>
<a href="#" panel-name="chronological-UrlShortenerTestSuite" class="navigator-link ">
<span>Chronological view</span>
</a> <!-- navigator-link -->
</li>
</ul>
</div> <!-- suite-section-content -->
<div class="result-section">
<div class="suite-section-title">
<span>Results</span>
</div> <!-- suite-section-title -->
<div class="suite-section-content">
<ul>
<li>
<span class="method-stats">3 methods, 3 passed</span>
</li>
<li>
<span class="method-list-title passed">Passed methods</span>
<span class="show-or-hide-methods passed">
<a href="#" panel-name="suite-UrlShortenerTestSuite" class="hide-methods passed suite-UrlShortenerTestSuite"> (hide)</a> <!-- hide-methods passed suite-UrlShortenerTestSuite -->
<a href="#" panel-name="suite-UrlShortenerTestSuite" class="show-methods passed suite-UrlShortenerTestSuite"> (show)</a> <!-- show-methods passed suite-UrlShortenerTestSuite -->
</span>
<div class="method-list-content passed suite-UrlShortenerTestSuite">
<span>
<img src="passed.png" width="3%"/>
<a href="#" panel-name="suite-UrlShortenerTestSuite" title="com.bitmutex.shortener.AnalyticsTest" class="method navigator-link" hash-for-method="testAnalyticsEntity">testAnalyticsEntity</a> <!-- method navigator-link -->
</span>
<br/>
<span>
<img src="passed.png" width="3%"/>
<a href="#" panel-name="suite-UrlShortenerTestSuite" title="com.bitmutex.shortener.AnalyticsControllerTest" class="method navigator-link" hash-for-method="testGetAnalyticsDataWithInvalidShortUrl">testGetAnalyticsDataWithInvalidShortUrl</a> <!-- method navigator-link -->
</span>
<br/>
<span>
<img src="passed.png" width="3%"/>
<a href="#" panel-name="suite-UrlShortenerTestSuite" title="com.bitmutex.shortener.AnalyticsControllerTest" class="method navigator-link" hash-for-method="testGetAnalyticsDataWithValidShortUrl">testGetAnalyticsDataWithValidShortUrl</a> <!-- method navigator-link -->
</span>
<br/>
</div> <!-- method-list-content passed suite-UrlShortenerTestSuite -->
</li>
</ul>
</div> <!-- suite-section-content -->
</div> <!-- result-section -->
</div> <!-- navigator-suite-content -->
</div> <!-- rounded-window -->
</div> <!-- suite -->
</div> <!-- navigator-root -->
<div class="wrapper">
<div class="main-panel-root">
<div panel-name="suite-UrlShortenerTestSuite" class="panel UrlShortenerTestSuite">
<div class="suite-UrlShortenerTestSuite-class-passed">
<div class="main-panel-header rounded-window-top">
<img src="passed.png"/>
<span class="class-name">com.bitmutex.shortener.AnalyticsTest</span>
</div> <!-- main-panel-header rounded-window-top -->
<div class="main-panel-content rounded-window-bottom">
<div class="method">
<div class="method-content">
<a name="testAnalyticsEntity">
</a> <!-- testAnalyticsEntity -->
<span class="method-name">testAnalyticsEntity</span>
</div> <!-- method-content -->
</div> <!-- method -->
</div> <!-- main-panel-content rounded-window-bottom -->
</div> <!-- suite-UrlShortenerTestSuite-class-passed -->
<div class="suite-UrlShortenerTestSuite-class-passed">
<div class="main-panel-header rounded-window-top">
<img src="passed.png"/>
<span class="class-name">com.bitmutex.shortener.AnalyticsControllerTest</span>
</div> <!-- main-panel-header rounded-window-top -->
<div class="main-panel-content rounded-window-bottom">
<div class="method">
<div class="method-content">
<a name="testGetAnalyticsDataWithInvalidShortUrl">
</a> <!-- testGetAnalyticsDataWithInvalidShortUrl -->
<span class="method-name">testGetAnalyticsDataWithInvalidShortUrl</span>
</div> <!-- method-content -->
</div> <!-- method -->
<div class="method">
<div class="method-content">
<a name="testGetAnalyticsDataWithValidShortUrl">
</a> <!-- testGetAnalyticsDataWithValidShortUrl -->
<span class="method-name">testGetAnalyticsDataWithValidShortUrl</span>
</div> <!-- method-content -->
</div> <!-- method -->
</div> <!-- main-panel-content rounded-window-bottom -->
</div> <!-- suite-UrlShortenerTestSuite-class-passed -->
</div> <!-- panel UrlShortenerTestSuite -->
<div panel-name="test-xml-UrlShortenerTestSuite" class="panel">
<div class="main-panel-header rounded-window-top">
<span class="header-content">C:\Users\bigwiz\IdeaProjects\URLShortener\src\test\resources\testng.xml</span>
</div> <!-- main-panel-header rounded-window-top -->
<div class="main-panel-content rounded-window-bottom">
<pre>
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE suite SYSTEM &quot;https://testng.org/testng-1.0.dtd&quot;&gt;
&lt;suite name=&quot;UrlShortenerTestSuite&quot; guice-stage=&quot;DEVELOPMENT&quot;&gt;
&lt;test thread-count=&quot;5&quot; name=&quot;UrlShortenerTestSuite&quot;&gt;
&lt;classes&gt;
&lt;class name=&quot;com.bitmutex.shortener.AnalyticsControllerTest&quot;/&gt;
&lt;class name=&quot;com.bitmutex.shortener.AnalyticsTest&quot;/&gt;
&lt;/classes&gt;
&lt;/test&gt; &lt;!-- UrlShortenerTestSuite --&gt;
&lt;/suite&gt; &lt;!-- UrlShortenerTestSuite --&gt;
</pre>
</div> <!-- main-panel-content rounded-window-bottom -->
</div> <!-- panel -->
<div panel-name="testlist-UrlShortenerTestSuite" class="panel">
<div class="main-panel-header rounded-window-top">
<span class="header-content">Tests for UrlShortenerTestSuite</span>
</div> <!-- main-panel-header rounded-window-top -->
<div class="main-panel-content rounded-window-bottom">
<ul>
<li>
<span class="test-name">UrlShortenerTestSuite (2 classes)</span>
</li>
</ul>
</div> <!-- main-panel-content rounded-window-bottom -->
</div> <!-- panel -->
<div panel-name="group-UrlShortenerTestSuite" class="panel">
<div class="main-panel-header rounded-window-top">
<span class="header-content">Groups for UrlShortenerTestSuite</span>
</div> <!-- main-panel-header rounded-window-top -->
<div class="main-panel-content rounded-window-bottom">
</div> <!-- main-panel-content rounded-window-bottom -->
</div> <!-- panel -->
<div panel-name="times-UrlShortenerTestSuite" class="panel">
<div class="main-panel-header rounded-window-top">
<span class="header-content">Times for UrlShortenerTestSuite</span>
</div> <!-- main-panel-header rounded-window-top -->
<div class="main-panel-content rounded-window-bottom">
<div class="times-div">
<script type="text/javascript">
suiteTableInitFunctions.push('tableData_UrlShortenerTestSuite');
function tableData_UrlShortenerTestSuite() {
var data = new google.visualization.DataTable();
data.addColumn('number', 'Number');
data.addColumn('string', 'Method');
data.addColumn('string', 'Class');
data.addColumn('number', 'Time (ms)');
data.addRows(3);
data.setCell(0, 0, 0)
data.setCell(0, 1, 'testGetAnalyticsDataWithInvalidShortUrl')
data.setCell(0, 2, 'com.bitmutex.shortener.AnalyticsControllerTest')
data.setCell(0, 3, 21);
data.setCell(1, 0, 1)
data.setCell(1, 1, 'testGetAnalyticsDataWithValidShortUrl')
data.setCell(1, 2, 'com.bitmutex.shortener.AnalyticsControllerTest')
data.setCell(1, 3, 5);
data.setCell(2, 0, 2)
data.setCell(2, 1, 'testAnalyticsEntity')
data.setCell(2, 2, 'com.bitmutex.shortener.AnalyticsTest')
data.setCell(2, 3, 0);
window.suiteTableData['UrlShortenerTestSuite']= { tableData: data, tableDiv: 'times-div-UrlShortenerTestSuite'}
return data;
}
</script>
<span class="suite-total-time">Total running time: 26 ms</span>
<div id="times-div-UrlShortenerTestSuite">
</div> <!-- times-div-UrlShortenerTestSuite -->
</div> <!-- times-div -->
</div> <!-- main-panel-content rounded-window-bottom -->
</div> <!-- panel -->
<div panel-name="reporter-UrlShortenerTestSuite" class="panel">
<div class="main-panel-header rounded-window-top">
<span class="header-content">Reporter output for UrlShortenerTestSuite</span>
</div> <!-- main-panel-header rounded-window-top -->
<div class="main-panel-content rounded-window-bottom">
</div> <!-- main-panel-content rounded-window-bottom -->
</div> <!-- panel -->
<div panel-name="ignored-methods-UrlShortenerTestSuite" class="panel">
<div class="main-panel-header rounded-window-top">
<span class="header-content">0 ignored methods</span>
</div> <!-- main-panel-header rounded-window-top -->
<div class="main-panel-content rounded-window-bottom">
</div> <!-- main-panel-content rounded-window-bottom -->
</div> <!-- panel -->
<div panel-name="chronological-UrlShortenerTestSuite" class="panel">
<div class="main-panel-header rounded-window-top">
<span class="header-content">Methods in chronological order</span>
</div> <!-- main-panel-header rounded-window-top -->
<div class="main-panel-content rounded-window-bottom">
<div class="chronological-class">
<div class="chronological-class-name">com.bitmutex.shortener.AnalyticsControllerTest</div> <!-- chronological-class-name -->
<div class="configuration-class before">
<span class="method-name">setup</span>
<span class="method-start">0 ms</span>
</div> <!-- configuration-class before -->
<div class="test-method">
<span class="method-name">testGetAnalyticsDataWithInvalidShortUrl</span>
<span class="method-start">761 ms</span>
</div> <!-- test-method -->
<div class="test-method">
<span class="method-name">testGetAnalyticsDataWithValidShortUrl</span>
<span class="method-start">783 ms</span>
</div> <!-- test-method -->
</div> <!-- chronological-class -->
<div class="chronological-class">
<div class="chronological-class-name">com.bitmutex.shortener.AnalyticsTest</div> <!-- chronological-class-name -->
<div class="configuration-class before">
<span class="method-name">setup</span>
<span class="method-start">789 ms</span>
</div> <!-- configuration-class before -->
<div class="test-method">
<span class="method-name">testAnalyticsEntity</span>
<span class="method-start">832 ms</span>
</div> <!-- test-method -->
</div> <!-- main-panel-content rounded-window-bottom -->
</div> <!-- panel -->
</div> <!-- main-panel-root -->
</div> <!-- wrapper -->
</body>
<script type="text/javascript" src="testng-reports2.js"></script>
</html>

2
test-output/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated by org.testng.reporters.JUnitReportReporter -->
<testsuite hostname="Wiz-Workstation" failures="0" tests="2" name="com.bitmutex.shortener.AnalyticsControllerTest" time="0.026" errors="0" timestamp="2024-01-18T04:17:29 IST" skipped="0">
<testcase classname="com.bitmutex.shortener.AnalyticsControllerTest" name="testGetAnalyticsDataWithValidShortUrl" time="0.005"/>
<system-out/>
<testcase classname="com.bitmutex.shortener.AnalyticsControllerTest" name="testGetAnalyticsDataWithInvalidShortUrl" time="0.021"/>
<system-out/>
</testsuite> <!-- com.bitmutex.shortener.AnalyticsControllerTest -->

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated by org.testng.reporters.JUnitReportReporter -->
<testsuite hostname="Wiz-Workstation" failures="0" tests="1" name="com.bitmutex.shortener.AnalyticsTest" time="0.000" errors="0" timestamp="2024-01-18T04:17:29 IST" skipped="0">
<testcase classname="com.bitmutex.shortener.AnalyticsTest" name="testAnalyticsEntity" time="0.000"/>
<system-out/>
</testsuite> <!-- com.bitmutex.shortener.AnalyticsTest -->

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

View file

@ -0,0 +1 @@
[SuiteResult context=URLShortener]

View file

@ -0,0 +1,62 @@
<table border='1'>
<tr>
<th>Class name</th>
<th>Method name</th>
<th>Groups</th>
</tr><tr>
<td>com.bitmutex.shortener.AnalyticsTest</td>
<td>&nbsp;</td><td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@Test</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>testAnalyticsEntity</td>
<td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@BeforeClass</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>setup</td>
<td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@BeforeMethod</td>
</tr>
<tr>
<td align='center' colspan='3'>@AfterMethod</td>
</tr>
<tr>
<td align='center' colspan='3'>@AfterClass</td>
</tr>
<tr>
<td>com.bitmutex.shortener.AnalyticsControllerTest</td>
<td>&nbsp;</td><td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@Test</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>testGetAnalyticsDataWithValidShortUrl</td>
<td>&nbsp;</td></tr>
<tr>
<td>&nbsp;</td>
<td>testGetAnalyticsDataWithInvalidShortUrl</td>
<td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@BeforeClass</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>setup</td>
<td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@BeforeMethod</td>
</tr>
<tr>
<td align='center' colspan='3'>@AfterMethod</td>
</tr>
<tr>
<td align='center' colspan='3'>@AfterClass</td>
</tr>
</table>

View file

@ -0,0 +1 @@
<h2>Groups used for this test run</h2>

View file

@ -0,0 +1,6 @@
<html><head><title>Results for Default Suite</title></head>
<frameset cols="26%,74%">
<frame src="toc.html" name="navFrame">
<frame src="main.html" name="mainFrame">
</frameset>
</html>

View file

@ -0,0 +1,2 @@
<html><head><title>Results for Default Suite</title></head>
<body>Select a result on the left-hand pane.</body></html>

View file

@ -0,0 +1,14 @@
<h2>Methods run, sorted chronologically</h2><h3>&gt;&gt; means before, &lt;&lt; means after</h3><p/><br/><em>Default Suite</em><p/><small><i>(Hover the method name to see the test class name)</i></small><p/>
<table border="1">
<tr><th>Time</th><th>Delta (ms)</th><th>Suite<br>configuration</th><th>Test<br>configuration</th><th>Class<br>configuration</th><th>Groups<br>configuration</th><th>Method<br>configuration</th><th>Test<br>method</th><th>Thread</th><th>Instances</th></tr>
<tr bgcolor="aaea95"> <td>24/01/18 03:45:09</td> <td>0</td> <td>&nbsp;</td><td>&nbsp;</td><td title="&gt;&gt;AnalyticsTest.setup()[pri:0, instance:com.bitmutex.shortener.AnalyticsTest@402e37bc]">&gt;&gt;setup</td>
<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td> <td>main@295530567</td> <td></td> </tr>
<tr bgcolor="fee8e7"> <td>24/01/18 03:45:09</td> <td>604</td> <td>&nbsp;</td><td>&nbsp;</td><td title="&gt;&gt;AnalyticsControllerTest.setup()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@7fc2413d]">&gt;&gt;setup</td>
<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td> <td>main@295530567</td> <td></td> </tr>
<tr bgcolor="aaea95"> <td>24/01/18 03:45:09</td> <td>592</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsTest.testAnalyticsEntity()[pri:0, instance:com.bitmutex.shortener.AnalyticsTest@402e37bc]">testAnalyticsEntity</td>
<td>main@295530567</td> <td></td> </tr>
<tr bgcolor="fee8e7"> <td>24/01/18 03:45:09</td> <td>797</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsControllerTest.testGetAnalyticsDataWithInvalidShortUrl()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@7fc2413d]">testGetAnalyticsDataWithInvalidShortUrl</td>
<td>main@295530567</td> <td></td> </tr>
<tr bgcolor="fee8e7"> <td>24/01/18 03:45:09</td> <td>819</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsControllerTest.testGetAnalyticsDataWithValidShortUrl()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@7fc2413d]">testGetAnalyticsDataWithValidShortUrl</td>
<td>main@295530567</td> <td></td> </tr>
</table>

View file

@ -0,0 +1,2 @@
<h2>Methods that were not run</h2><table>
</table>

View file

@ -0,0 +1,14 @@
<h2>Methods run, sorted chronologically</h2><h3>&gt;&gt; means before, &lt;&lt; means after</h3><p/><br/><em>Default Suite</em><p/><small><i>(Hover the method name to see the test class name)</i></small><p/>
<table border="1">
<tr><th>Time</th><th>Delta (ms)</th><th>Suite<br>configuration</th><th>Test<br>configuration</th><th>Class<br>configuration</th><th>Groups<br>configuration</th><th>Method<br>configuration</th><th>Test<br>method</th><th>Thread</th><th>Instances</th></tr>
<tr bgcolor="aaea95"> <td>24/01/18 03:45:09</td> <td>0</td> <td>&nbsp;</td><td>&nbsp;</td><td title="&gt;&gt;AnalyticsTest.setup()[pri:0, instance:com.bitmutex.shortener.AnalyticsTest@402e37bc]">&gt;&gt;setup</td>
<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td> <td>main@295530567</td> <td></td> </tr>
<tr bgcolor="fee8e7"> <td>24/01/18 03:45:09</td> <td>819</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsControllerTest.testGetAnalyticsDataWithValidShortUrl()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@7fc2413d]">testGetAnalyticsDataWithValidShortUrl</td>
<td>main@295530567</td> <td></td> </tr>
<tr bgcolor="fee8e7"> <td>24/01/18 03:45:09</td> <td>604</td> <td>&nbsp;</td><td>&nbsp;</td><td title="&gt;&gt;AnalyticsControllerTest.setup()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@7fc2413d]">&gt;&gt;setup</td>
<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td> <td>main@295530567</td> <td></td> </tr>
<tr bgcolor="fee8e7"> <td>24/01/18 03:45:09</td> <td>797</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsControllerTest.testGetAnalyticsDataWithInvalidShortUrl()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@7fc2413d]">testGetAnalyticsDataWithInvalidShortUrl</td>
<td>main@295530567</td> <td></td> </tr>
<tr bgcolor="aaea95"> <td>24/01/18 03:45:09</td> <td>592</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsTest.testAnalyticsEntity()[pri:0, instance:com.bitmutex.shortener.AnalyticsTest@402e37bc]">testAnalyticsEntity</td>
<td>main@295530567</td> <td></td> </tr>
</table>

View file

@ -0,0 +1 @@
<h2>Reporter output</h2><table></table>

View file

@ -0,0 +1 @@
<html><head><title>testng.xml for Default Suite</title></head><body><tt>&lt;?xml&nbsp;version="1.0"&nbsp;encoding="UTF-8"?&gt; <br/>&lt;!DOCTYPE&nbsp;suite&nbsp;SYSTEM&nbsp;"https://testng.org/testng-1.0.dtd"&gt; <br/>&lt;suite&nbsp;name="Default&nbsp;Suite"&nbsp;guice-stage="DEVELOPMENT"&gt; <br/>&nbsp;&nbsp;&lt;test&nbsp;thread-count="5"&nbsp;name="URLShortener"&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;classes&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;class&nbsp;name="com.bitmutex.shortener.AnalyticsTest"/&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;class&nbsp;name="com.bitmutex.shortener.AnalyticsControllerTest"/&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/classes&gt; <br/>&nbsp;&nbsp;&lt;/test&gt;&nbsp;&lt;!--&nbsp;URLShortener&nbsp;--&gt; <br/>&lt;/suite&gt;&nbsp;&lt;!--&nbsp;Default&nbsp;Suite&nbsp;--&gt; <br/></tt></body></html>

View file

@ -0,0 +1,30 @@
<html>
<head>
<title>Results for Default Suite</title>
<link href="../testng.css" rel="stylesheet" type="text/css" />
<link href="../my-testng.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h3><p align="center">Results for<br/><em>Default Suite</em></p></h3>
<table border='1' width='100%'>
<tr valign='top'>
<td>1 test</td>
<td><a target='mainFrame' href='classes.html'>2 classes</a></td>
<td>3 methods:<br/>
&nbsp;&nbsp;<a target='mainFrame' href='methods.html'>chronological</a><br/>
&nbsp;&nbsp;<a target='mainFrame' href='methods-alphabetical.html'>alphabetical</a><br/>
&nbsp;&nbsp;<a target='mainFrame' href='methods-not-run.html'>not run (0)</a></td>
</tr>
<tr>
<td><a target='mainFrame' href='groups.html'>0 group</a></td>
<td><a target='mainFrame' href='reporter-output.html'>reporter output</a></td>
<td><a target='mainFrame' href='testng.xml.html'>testng.xml</a></td>
</tr></table>
<table width='100%' class='test-passed'>
<tr><td>
<table style='width: 100%'><tr><td valign='top'>URLShortener (3/0/0)</td><td valign='top' align='right'>
<a href='URLShortener.html' target='mainFrame'>Results</a>
</td></tr></table>
</td></tr><p/>
</table>
</body></html>

View file

@ -0,0 +1 @@
[SuiteResult context=MyTestClass]

View file

@ -0,0 +1,62 @@
<table border='1'>
<tr>
<th>Class name</th>
<th>Method name</th>
<th>Groups</th>
</tr><tr>
<td>com.bitmutex.shortener.AnalyticsTest</td>
<td>&nbsp;</td><td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@Test</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>testAnalyticsEntity</td>
<td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@BeforeClass</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>setup</td>
<td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@BeforeMethod</td>
</tr>
<tr>
<td align='center' colspan='3'>@AfterMethod</td>
</tr>
<tr>
<td align='center' colspan='3'>@AfterClass</td>
</tr>
<tr>
<td>com.bitmutex.shortener.AnalyticsControllerTest</td>
<td>&nbsp;</td><td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@Test</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>testGetAnalyticsDataWithValidShortUrl</td>
<td>&nbsp;</td></tr>
<tr>
<td>&nbsp;</td>
<td>testGetAnalyticsDataWithInvalidShortUrl</td>
<td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@BeforeClass</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>setup</td>
<td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@BeforeMethod</td>
</tr>
<tr>
<td align='center' colspan='3'>@AfterMethod</td>
</tr>
<tr>
<td align='center' colspan='3'>@AfterClass</td>
</tr>
</table>

View file

@ -0,0 +1 @@
<h2>Groups used for this test run</h2>

View file

@ -0,0 +1,6 @@
<html><head><title>Results for MyTestSuite</title></head>
<frameset cols="26%,74%">
<frame src="toc.html" name="navFrame">
<frame src="main.html" name="mainFrame">
</frameset>
</html>

View file

@ -0,0 +1,2 @@
<html><head><title>Results for MyTestSuite</title></head>
<body>Select a result on the left-hand pane.</body></html>

View file

@ -0,0 +1,14 @@
<h2>Methods run, sorted chronologically</h2><h3>&gt;&gt; means before, &lt;&lt; means after</h3><p/><br/><em>MyTestSuite</em><p/><small><i>(Hover the method name to see the test class name)</i></small><p/>
<table border="1">
<tr><th>Time</th><th>Delta (ms)</th><th>Suite<br>configuration</th><th>Test<br>configuration</th><th>Class<br>configuration</th><th>Groups<br>configuration</th><th>Method<br>configuration</th><th>Test<br>method</th><th>Thread</th><th>Instances</th></tr>
<tr bgcolor="77a1ed"> <td>24/01/18 04:17:09</td> <td>0</td> <td>&nbsp;</td><td>&nbsp;</td><td title="&gt;&gt;AnalyticsTest.setup()[pri:0, instance:com.bitmutex.shortener.AnalyticsTest@11a9e7c8]">&gt;&gt;setup</td>
<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td> <td>main@295530567</td> <td></td> </tr>
<tr bgcolor="d08175"> <td>24/01/18 04:17:08</td> <td>-768</td> <td>&nbsp;</td><td>&nbsp;</td><td title="&gt;&gt;AnalyticsControllerTest.setup()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@233fe9b6]">&gt;&gt;setup</td>
<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td> <td>main@295530567</td> <td></td> </tr>
<tr bgcolor="77a1ed"> <td>24/01/18 04:17:09</td> <td>41</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsTest.testAnalyticsEntity()[pri:0, instance:com.bitmutex.shortener.AnalyticsTest@11a9e7c8]">testAnalyticsEntity</td>
<td>main@295530567</td> <td></td> </tr>
<tr bgcolor="d08175"> <td>24/01/18 04:17:09</td> <td>-23</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsControllerTest.testGetAnalyticsDataWithInvalidShortUrl()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@233fe9b6]">testGetAnalyticsDataWithInvalidShortUrl</td>
<td>main@295530567</td> <td></td> </tr>
<tr bgcolor="d08175"> <td>24/01/18 04:17:09</td> <td>-6</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsControllerTest.testGetAnalyticsDataWithValidShortUrl()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@233fe9b6]">testGetAnalyticsDataWithValidShortUrl</td>
<td>main@295530567</td> <td></td> </tr>
</table>

View file

@ -0,0 +1,2 @@
<h2>Methods that were not run</h2><table>
</table>

View file

@ -0,0 +1,14 @@
<h2>Methods run, sorted chronologically</h2><h3>&gt;&gt; means before, &lt;&lt; means after</h3><p/><br/><em>MyTestSuite</em><p/><small><i>(Hover the method name to see the test class name)</i></small><p/>
<table border="1">
<tr><th>Time</th><th>Delta (ms)</th><th>Suite<br>configuration</th><th>Test<br>configuration</th><th>Class<br>configuration</th><th>Groups<br>configuration</th><th>Method<br>configuration</th><th>Test<br>method</th><th>Thread</th><th>Instances</th></tr>
<tr bgcolor="77a1ed"> <td>24/01/18 04:17:09</td> <td>0</td> <td>&nbsp;</td><td>&nbsp;</td><td title="&gt;&gt;AnalyticsTest.setup()[pri:0, instance:com.bitmutex.shortener.AnalyticsTest@11a9e7c8]">&gt;&gt;setup</td>
<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td> <td>main@295530567</td> <td></td> </tr>
<tr bgcolor="77a1ed"> <td>24/01/18 04:17:09</td> <td>41</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsTest.testAnalyticsEntity()[pri:0, instance:com.bitmutex.shortener.AnalyticsTest@11a9e7c8]">testAnalyticsEntity</td>
<td>main@295530567</td> <td></td> </tr>
<tr bgcolor="d08175"> <td>24/01/18 04:17:09</td> <td>-6</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsControllerTest.testGetAnalyticsDataWithValidShortUrl()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@233fe9b6]">testGetAnalyticsDataWithValidShortUrl</td>
<td>main@295530567</td> <td></td> </tr>
<tr bgcolor="d08175"> <td>24/01/18 04:17:08</td> <td>-768</td> <td>&nbsp;</td><td>&nbsp;</td><td title="&gt;&gt;AnalyticsControllerTest.setup()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@233fe9b6]">&gt;&gt;setup</td>
<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td> <td>main@295530567</td> <td></td> </tr>
<tr bgcolor="d08175"> <td>24/01/18 04:17:09</td> <td>-23</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsControllerTest.testGetAnalyticsDataWithInvalidShortUrl()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@233fe9b6]">testGetAnalyticsDataWithInvalidShortUrl</td>
<td>main@295530567</td> <td></td> </tr>
</table>

View file

@ -0,0 +1 @@
<h2>Reporter output</h2><table></table>

View file

@ -0,0 +1 @@
<html><head><title>testng.xml for MyTestSuite</title></head><body><tt>&lt;?xml&nbsp;version="1.0"&nbsp;encoding="UTF-8"?&gt; <br/>&lt;!DOCTYPE&nbsp;suite&nbsp;SYSTEM&nbsp;"https://testng.org/testng-1.0.dtd"&gt; <br/>&lt;suite&nbsp;name="MyTestSuite"&nbsp;guice-stage="DEVELOPMENT"&gt; <br/>&nbsp;&nbsp;&lt;test&nbsp;thread-count="5"&nbsp;name="MyTestClass"&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;classes&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;class&nbsp;name="com.bitmutex.shortener.AnalyticsControllerTest"/&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;class&nbsp;name="com.bitmutex.shortener.AnalyticsTest"/&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/classes&gt; <br/>&nbsp;&nbsp;&lt;/test&gt;&nbsp;&lt;!--&nbsp;MyTestClass&nbsp;--&gt; <br/>&lt;/suite&gt;&nbsp;&lt;!--&nbsp;MyTestSuite&nbsp;--&gt; <br/></tt></body></html>

View file

@ -0,0 +1,30 @@
<html>
<head>
<title>Results for MyTestSuite</title>
<link href="../testng.css" rel="stylesheet" type="text/css" />
<link href="../my-testng.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h3><p align="center">Results for<br/><em>MyTestSuite</em></p></h3>
<table border='1' width='100%'>
<tr valign='top'>
<td>1 test</td>
<td><a target='mainFrame' href='classes.html'>2 classes</a></td>
<td>3 methods:<br/>
&nbsp;&nbsp;<a target='mainFrame' href='methods.html'>chronological</a><br/>
&nbsp;&nbsp;<a target='mainFrame' href='methods-alphabetical.html'>alphabetical</a><br/>
&nbsp;&nbsp;<a target='mainFrame' href='methods-not-run.html'>not run (0)</a></td>
</tr>
<tr>
<td><a target='mainFrame' href='groups.html'>0 group</a></td>
<td><a target='mainFrame' href='reporter-output.html'>reporter output</a></td>
<td><a target='mainFrame' href='testng.xml.html'>testng.xml</a></td>
</tr></table>
<table width='100%' class='test-passed'>
<tr><td>
<table style='width: 100%'><tr><td valign='top'>MyTestClass (3/0/0)</td><td valign='top' align='right'>
<a href='MyTestClass.html' target='mainFrame'>Results</a>
</td></tr></table>
</td></tr><p/>
</table>
</body></html>

View file

@ -0,0 +1 @@
[SuiteResult context=UrlShortenerTestSuite]

View file

@ -0,0 +1,62 @@
<table border='1'>
<tr>
<th>Class name</th>
<th>Method name</th>
<th>Groups</th>
</tr><tr>
<td>com.bitmutex.shortener.AnalyticsTest</td>
<td>&nbsp;</td><td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@Test</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>testAnalyticsEntity</td>
<td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@BeforeClass</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>setup</td>
<td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@BeforeMethod</td>
</tr>
<tr>
<td align='center' colspan='3'>@AfterMethod</td>
</tr>
<tr>
<td align='center' colspan='3'>@AfterClass</td>
</tr>
<tr>
<td>com.bitmutex.shortener.AnalyticsControllerTest</td>
<td>&nbsp;</td><td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@Test</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>testGetAnalyticsDataWithValidShortUrl</td>
<td>&nbsp;</td></tr>
<tr>
<td>&nbsp;</td>
<td>testGetAnalyticsDataWithInvalidShortUrl</td>
<td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@BeforeClass</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>setup</td>
<td>&nbsp;</td></tr>
<tr>
<td align='center' colspan='3'>@BeforeMethod</td>
</tr>
<tr>
<td align='center' colspan='3'>@AfterMethod</td>
</tr>
<tr>
<td align='center' colspan='3'>@AfterClass</td>
</tr>
</table>

View file

@ -0,0 +1 @@
<h2>Groups used for this test run</h2>

View file

@ -0,0 +1,6 @@
<html><head><title>Results for UrlShortenerTestSuite</title></head>
<frameset cols="26%,74%">
<frame src="toc.html" name="navFrame">
<frame src="main.html" name="mainFrame">
</frameset>
</html>

View file

@ -0,0 +1,2 @@
<html><head><title>Results for UrlShortenerTestSuite</title></head>
<body>Select a result on the left-hand pane.</body></html>

View file

@ -0,0 +1,14 @@
<h2>Methods run, sorted chronologically</h2><h3>&gt;&gt; means before, &lt;&lt; means after</h3><p/><br/><em>UrlShortenerTestSuite</em><p/><small><i>(Hover the method name to see the test class name)</i></small><p/>
<table border="1">
<tr><th>Time</th><th>Delta (ms)</th><th>Suite<br>configuration</th><th>Test<br>configuration</th><th>Class<br>configuration</th><th>Groups<br>configuration</th><th>Method<br>configuration</th><th>Test<br>method</th><th>Thread</th><th>Instances</th></tr>
<tr bgcolor="aaea95"> <td>24/01/18 04:17:28</td> <td>0</td> <td>&nbsp;</td><td>&nbsp;</td><td title="&gt;&gt;AnalyticsControllerTest.setup()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@402e37bc]">&gt;&gt;setup</td>
<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td> <td>main@295530567</td> <td></td> </tr>
<tr bgcolor="fee8e7"> <td>24/01/18 04:17:29</td> <td>786</td> <td>&nbsp;</td><td>&nbsp;</td><td title="&gt;&gt;AnalyticsTest.setup()[pri:0, instance:com.bitmutex.shortener.AnalyticsTest@7fc2413d]">&gt;&gt;setup</td>
<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td> <td>main@295530567</td> <td></td> </tr>
<tr bgcolor="fee8e7"> <td>24/01/18 04:17:29</td> <td>829</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsTest.testAnalyticsEntity()[pri:0, instance:com.bitmutex.shortener.AnalyticsTest@7fc2413d]">testAnalyticsEntity</td>
<td>main@295530567</td> <td></td> </tr>
<tr bgcolor="aaea95"> <td>24/01/18 04:17:29</td> <td>758</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsControllerTest.testGetAnalyticsDataWithInvalidShortUrl()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@402e37bc]">testGetAnalyticsDataWithInvalidShortUrl</td>
<td>main@295530567</td> <td></td> </tr>
<tr bgcolor="aaea95"> <td>24/01/18 04:17:29</td> <td>780</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsControllerTest.testGetAnalyticsDataWithValidShortUrl()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@402e37bc]">testGetAnalyticsDataWithValidShortUrl</td>
<td>main@295530567</td> <td></td> </tr>
</table>

View file

@ -0,0 +1,2 @@
<h2>Methods that were not run</h2><table>
</table>

View file

@ -0,0 +1,14 @@
<h2>Methods run, sorted chronologically</h2><h3>&gt;&gt; means before, &lt;&lt; means after</h3><p/><br/><em>UrlShortenerTestSuite</em><p/><small><i>(Hover the method name to see the test class name)</i></small><p/>
<table border="1">
<tr><th>Time</th><th>Delta (ms)</th><th>Suite<br>configuration</th><th>Test<br>configuration</th><th>Class<br>configuration</th><th>Groups<br>configuration</th><th>Method<br>configuration</th><th>Test<br>method</th><th>Thread</th><th>Instances</th></tr>
<tr bgcolor="aaea95"> <td>24/01/18 04:17:28</td> <td>0</td> <td>&nbsp;</td><td>&nbsp;</td><td title="&gt;&gt;AnalyticsControllerTest.setup()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@402e37bc]">&gt;&gt;setup</td>
<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td> <td>main@295530567</td> <td></td> </tr>
<tr bgcolor="aaea95"> <td>24/01/18 04:17:29</td> <td>780</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsControllerTest.testGetAnalyticsDataWithValidShortUrl()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@402e37bc]">testGetAnalyticsDataWithValidShortUrl</td>
<td>main@295530567</td> <td></td> </tr>
<tr bgcolor="fee8e7"> <td>24/01/18 04:17:29</td> <td>829</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsTest.testAnalyticsEntity()[pri:0, instance:com.bitmutex.shortener.AnalyticsTest@7fc2413d]">testAnalyticsEntity</td>
<td>main@295530567</td> <td></td> </tr>
<tr bgcolor="aaea95"> <td>24/01/18 04:17:29</td> <td>758</td> <td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td title="AnalyticsControllerTest.testGetAnalyticsDataWithInvalidShortUrl()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@402e37bc]">testGetAnalyticsDataWithInvalidShortUrl</td>
<td>main@295530567</td> <td></td> </tr>
<tr bgcolor="fee8e7"> <td>24/01/18 04:17:29</td> <td>786</td> <td>&nbsp;</td><td>&nbsp;</td><td title="&gt;&gt;AnalyticsTest.setup()[pri:0, instance:com.bitmutex.shortener.AnalyticsTest@7fc2413d]">&gt;&gt;setup</td>
<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td> <td>main@295530567</td> <td></td> </tr>
</table>

View file

@ -0,0 +1 @@
<h2>Reporter output</h2><table></table>

View file

@ -0,0 +1 @@
<html><head><title>testng.xml for UrlShortenerTestSuite</title></head><body><tt>&lt;?xml&nbsp;version="1.0"&nbsp;encoding="UTF-8"?&gt; <br/>&lt;!DOCTYPE&nbsp;suite&nbsp;SYSTEM&nbsp;"https://testng.org/testng-1.0.dtd"&gt; <br/>&lt;suite&nbsp;name="UrlShortenerTestSuite"&nbsp;guice-stage="DEVELOPMENT"&gt; <br/>&nbsp;&nbsp;&lt;test&nbsp;thread-count="5"&nbsp;name="UrlShortenerTestSuite"&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;classes&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;class&nbsp;name="com.bitmutex.shortener.AnalyticsControllerTest"/&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;class&nbsp;name="com.bitmutex.shortener.AnalyticsTest"/&gt; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/classes&gt; <br/>&nbsp;&nbsp;&lt;/test&gt;&nbsp;&lt;!--&nbsp;UrlShortenerTestSuite&nbsp;--&gt; <br/>&lt;/suite&gt;&nbsp;&lt;!--&nbsp;UrlShortenerTestSuite&nbsp;--&gt; <br/></tt></body></html>

View file

@ -0,0 +1,30 @@
<html>
<head>
<title>Results for UrlShortenerTestSuite</title>
<link href="../testng.css" rel="stylesheet" type="text/css" />
<link href="../my-testng.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h3><p align="center">Results for<br/><em>UrlShortenerTestSuite</em></p></h3>
<table border='1' width='100%'>
<tr valign='top'>
<td>1 test</td>
<td><a target='mainFrame' href='classes.html'>2 classes</a></td>
<td>3 methods:<br/>
&nbsp;&nbsp;<a target='mainFrame' href='methods.html'>chronological</a><br/>
&nbsp;&nbsp;<a target='mainFrame' href='methods-alphabetical.html'>alphabetical</a><br/>
&nbsp;&nbsp;<a target='mainFrame' href='methods-not-run.html'>not run (0)</a></td>
</tr>
<tr>
<td><a target='mainFrame' href='groups.html'>0 group</a></td>
<td><a target='mainFrame' href='reporter-output.html'>reporter output</a></td>
<td><a target='mainFrame' href='testng.xml.html'>testng.xml</a></td>
</tr></table>
<table width='100%' class='test-passed'>
<tr><td>
<table style='width: 100%'><tr><td valign='top'>UrlShortenerTestSuite (3/0/0)</td><td valign='top' align='right'>
<a href='UrlShortenerTestSuite.html' target='mainFrame'>Results</a>
</td></tr></table>
</td></tr><p/>
</table>
</body></html>

View file

@ -0,0 +1,9 @@
<html>
<head><title></title><link href="./testng.css" rel="stylesheet" type="text/css" />
<link href="./my-testng.css" rel="stylesheet" type="text/css" />
</head><body>
<h2><p align='center'>Test results</p></h2>
<table border='1' width='100%' class='main-page'><tr><th>Suite</th><th>Passed</th><th>Failed</th><th>Skipped</th><th>testng.xml</th></tr>
<tr align='center' class='invocation-passed'><td><em>Total</em></td><td><em>3</em></td><td><em>0</em></td><td><em>0</em></td><td>&nbsp;</td></tr>
<tr align='center' class='invocation-passed'><td><a href='UrlShortenerTestSuite/index.html'>UrlShortenerTestSuite</a></td>
<td>3</td><td>0</td><td>0</td><td><a href='UrlShortenerTestSuite/testng.xml.html'>Link</a></td></tr></table></body></html>

BIN
test-output/passed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,019 B

BIN
test-output/skipped.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

View file

@ -0,0 +1,326 @@
body {
margin: 0 0 5px 5px;
}
ul {
margin: 0;
}
li {
list-style-type: none;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.navigator-selected {
background: #ffa500;
}
.wrapper {
position: absolute;
top: 60px;
bottom: 0;
left: 400px;
right: 0;
overflow: auto;
}
.navigator-root {
position: absolute;
top: 60px;
bottom: 0;
left: 0;
width: 400px;
overflow-y: auto;
}
.suite {
margin: 0 10px 10px 0;
background-color: #fff8dc;
}
.suite-name {
padding-left: 10px;
font-size: 25px;
font-family: Times, sans-serif;
}
.main-panel-header {
padding: 5px;
background-color: #9FB4D9; /*afeeee*/;
font-family: monospace;
font-size: 18px;
}
.main-panel-content {
padding: 5px;
margin-bottom: 10px;
background-color: #DEE8FC; /*d0ffff*/;
}
.rounded-window {
border-radius: 10px;
border-style: solid;
border-width: 1px;
}
.rounded-window-top {
border-top-right-radius: 10px 10px;
border-top-left-radius: 10px 10px;
border-style: solid;
border-width: 1px;
overflow: auto;
}
.light-rounded-window-top {
border-top-right-radius: 10px 10px;
border-top-left-radius: 10px 10px;
}
.rounded-window-bottom {
border-style: solid;
border-width: 0 1px 1px 1px;
border-bottom-right-radius: 10px 10px;
border-bottom-left-radius: 10px 10px;
overflow: auto;
}
.method-name {
font-size: 12px;
font-family: monospace;
}
.method-content {
border-style: solid;
border-width: 0 0 1px 0;
margin-bottom: 10px;
padding-bottom: 5px;
width: 80%;
}
.parameters {
font-size: 14px;
font-family: monospace;
}
.stack-trace {
white-space: pre;
font-family: monospace;
font-size: 12px;
font-weight: bold;
margin-top: 0;
margin-left: 20px;
}
.testng-xml {
font-family: monospace;
}
.method-list-content {
margin-left: 10px;
}
.navigator-suite-content {
margin-left: 10px;
font: 12px 'Lucida Grande';
}
.suite-section-title {
margin-top: 10px;
width: 80%;
border-style: solid;
border-width: 1px 0 0 0;
font-family: Times, sans-serif;
font-size: 18px;
font-weight: bold;
}
.suite-section-content {
list-style-image: url(bullet_point.png);
}
.top-banner-root {
position: absolute;
top: 0;
height: 45px;
left: 0;
right: 0;
padding: 5px;
margin: 0 0 5px 0;
background-color: #0066ff;
font-family: Times, sans-serif;
color: #fff;
text-align: center;
}
.button{
position: absolute;
margin-left:500px;
margin-top:8px;
background-color: white;
color:#0066ff;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight:bold;
border-color:#0066ff ;
border-radius:25px;
cursor: pointer;
height:30px;
width:150px;
outline:none;
}
.top-banner-title-font {
font-size: 25px;
}
.test-name {
font-family: 'Lucida Grande', sans-serif;
font-size: 16px;
}
.suite-icon {
padding: 5px;
float: right;
height: 20px;
}
.test-group {
font: 20px 'Lucida Grande';
margin: 5px 5px 10px 5px;
border-width: 0 0 1px 0;
border-style: solid;
padding: 5px;
}
.test-group-name {
font-weight: bold;
}
.method-in-group {
font-size: 16px;
margin-left: 80px;
}
table.google-visualization-table-table {
width: 100%;
}
.reporter-method-name {
font-size: 14px;
font-family: monospace;
}
.reporter-method-output-div {
padding: 5px;
margin: 0 0 5px 20px;
font-size: 12px;
font-family: monospace;
border-width: 0 0 0 1px;
border-style: solid;
}
.ignored-class-div {
font-size: 14px;
font-family: monospace;
}
.ignored-methods-div {
padding: 5px;
margin: 0 0 5px 20px;
font-size: 12px;
font-family: monospace;
border-width: 0 0 0 1px;
border-style: solid;
}
.border-failed {
border-top-left-radius: 10px 10px;
border-bottom-left-radius: 10px 10px;
border-style: solid;
border-width: 0 0 0 10px;
border-color: #f00;
}
.border-skipped {
border-top-left-radius: 10px 10px;
border-bottom-left-radius: 10px 10px;
border-style: solid;
border-width: 0 0 0 10px;
border-color: #edc600;
}
.border-passed {
border-top-left-radius: 10px 10px;
border-bottom-left-radius: 10px 10px;
border-style: solid;
border-width: 0 0 0 10px;
border-color: #19f52d;
}
.times-div {
text-align: center;
padding: 5px;
}
.suite-total-time {
font: 16px 'Lucida Grande';
}
.configuration-suite {
margin-left: 20px;
}
.configuration-test {
margin-left: 40px;
}
.configuration-class {
margin-left: 60px;
}
.configuration-method {
margin-left: 80px;
}
.test-method {
margin-left: 100px;
}
.chronological-class {
background-color: skyblue;
border-style: solid;
border-width: 0 0 1px 1px;
}
.method-start {
float: right;
}
.chronological-class-name {
padding: 0 0 0 5px;
color: #008;
}
.after, .before, .test-method {
font-family: monospace;
font-size: 14px;
}
.navigator-suite-header {
font-size: 22px;
margin: 0 10px 5px 0;
background-color: #deb887;
text-align: center;
}
.collapse-all-icon {
padding: 5px;
float: right;
}
/*retro Theme*/

View file

@ -0,0 +1,122 @@
$(document).ready(function() {
$('a.navigator-link').on("click", function() {
// Extract the panel for this link
var panel = getPanelName($(this));
// Mark this link as currently selected
$('.navigator-link').parent().removeClass('navigator-selected');
$(this).parent().addClass('navigator-selected');
showPanel(panel);
});
installMethodHandlers('failed');
installMethodHandlers('skipped');
installMethodHandlers('passed', true); // hide passed methods by default
$('a.method').on("click", function() {
showMethod($(this));
return false;
});
// Hide all the panels and display the first one (do this last
// to make sure the click() will invoke the listeners)
$('.panel').hide();
$('.navigator-link').first().trigger("click");
// Collapse/expand the suites
$('a.collapse-all-link').on("click", function() {
var contents = $('.navigator-suite-content');
if (contents.css('display') == 'none') {
contents.show();
} else {
contents.hide();
}
});
});
// The handlers that take care of showing/hiding the methods
function installMethodHandlers(name, hide) {
function getContent(t) {
return $('.method-list-content.' + name + "." + t.attr('panel-name'));
}
function getHideLink(t, name) {
var s = 'a.hide-methods.' + name + "." + t.attr('panel-name');
return $(s);
}
function getShowLink(t, name) {
return $('a.show-methods.' + name + "." + t.attr('panel-name'));
}
function getMethodPanelClassSel(element, name) {
var panelName = getPanelName(element);
var sel = '.' + panelName + "-class-" + name;
return $(sel);
}
$('a.hide-methods.' + name).on("click", function() {
var w = getContent($(this));
w.hide();
getHideLink($(this), name).hide();
getShowLink($(this), name).show();
getMethodPanelClassSel($(this), name).hide();
});
$('a.show-methods.' + name).on("click", function() {
var w = getContent($(this));
w.show();
getHideLink($(this), name).show();
getShowLink($(this), name).hide();
showPanel(getPanelName($(this)));
getMethodPanelClassSel($(this), name).show();
});
if (hide) {
$('a.hide-methods.' + name).trigger("click");
} else {
$('a.show-methods.' + name).trigger("click");
}
}
function getHashForMethod(element) {
return element.attr('hash-for-method');
}
function getPanelName(element) {
return element.attr('panel-name');
}
function showPanel(panelName) {
$('.panel').hide();
var panel = $('.panel[panel-name="' + panelName + '"]');
panel.show();
}
function showMethod(element) {
var hashTag = getHashForMethod(element);
var panelName = getPanelName(element);
showPanel(panelName);
var current = document.location.href;
var base = current.substring(0, current.indexOf('#'))
document.location.href = base + '#' + hashTag;
var newPosition = $(document).scrollTop() - 65;
$(document).scrollTop(newPosition);
}
function drawTable() {
for (var i = 0; i < suiteTableInitFunctions.length; i++) {
window[suiteTableInitFunctions[i]]();
}
for (var k in window.suiteTableData) {
var v = window.suiteTableData[k];
var div = v.tableDiv;
var data = v.tableData
var table = new google.visualization.Table(document.getElementById(div));
table.draw(data, {
showRowNumber : false
});
}
}

View file

@ -0,0 +1,344 @@
body {
background-color: whitesmoke;
margin: 0 0 5px 5px;
}
ul {
margin-top: 10px;
margin-left:-10px;
}
li {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
padding:5px 5px;
}
a {
text-decoration: none;
color: black;
font-size: 14px;
}
a:hover {
color:black ;
text-decoration: underline;
}
.navigator-selected {
/* #ffa500; Mouse hover color after click Orange.*/
background:#027368
}
.wrapper {
position: absolute;
top: 60px;
bottom: 0;
left: 400px;
right: 0;
margin-right:9px;
overflow: auto;/*imortant*/
}
.navigator-root {
position: absolute;
top: 60px;
bottom: 0;
left: 0;
width: 400px;
overflow-y: auto;/*important*/
}
.suite {
margin: -5px 10px 10px 5px;
background-color: whitesmoke ;/*Colour of the left bside box*/
}
.suite-name {
font-size: 24px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;/*All TEST SUITE*/
color: white;
}
.main-panel-header {
padding: 5px;
background-color: #027368; /*afeeee*/;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
color:white;
font-size: 18px;
}
.main-panel-content {
padding: 5px;
margin-bottom: 10px;
background-color: #CCD0D1; /*d0ffff*/; /*Belongs to backGround of rightSide boxes*/
}
.rounded-window {
border-style: dotted;
border-width: 1px;/*Border of left Side box*/
background-color: whitesmoke;
border-radius: 10px;
}
.rounded-window-top {
border-top-right-radius: 10px 10px;
border-top-left-radius: 10px 10px;
border-style: solid;
border-width: 1px;
overflow: auto;/*Top of RightSide box*/
}
.light-rounded-window-top {
background-color: #027368;
padding-left:120px;
border-radius: 10px;
}
.rounded-window-bottom {
border-bottom-right-radius: 10px 10px;
border-bottom-left-radius: 10px 10px;
overflow: auto;/*Bottom of rightSide box*/
}
.method-name {
font-size: 14px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight: bold;
}
.method-content {
border-style: solid;
border-width: 0 0 1px 0;
margin-bottom: 10px;
padding-bottom: 5px;
width: 100%;
}
.parameters {
font-size: 14px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.stack-trace {
white-space: pre;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 12px;
font-weight: bold;
margin-top: 0;
margin-left: 20px; /*Error Stack Trace Message*/
}
.testng-xml {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.method-list-content {
margin-left: 10px;
}
.navigator-suite-content {
margin-left: 10px;
font: 12px 'Lucida Grande';
}
.suite-section-title {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
font-weight:bold;
background-color: #8C8887;
margin-left: -10px;
margin-top:10px;
padding:6px;
}
.suite-section-content {
list-style-image: url(bullet_point.png);
background-color: whitesmoke;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
overflow: hidden;
}
.top-banner-root {
position: absolute;
top: 0;
height: 45px;
left: 0;
right: 0;
padding: 5px;
margin: 0 0 5px 0;
background-color: #027368;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 18px;
color: #fff;
text-align: center;/*Belongs to the Top of Report*//*Status: - Completed*/
}
.top-banner-title-font {
font-size: 25px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
padding: 3px;
float: right;
}
.test-name {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 16px;
}
.suite-icon {
padding: 5px;
float: right;
height: 20px;
}
.test-group {
font: 20px 'Lucida Grande';
margin: 5px 5px 10px 5px;
border-width: 0 0 1px 0;
border-style: solid;
padding: 5px;
}
.test-group-name {
font-weight: bold;
}
.method-in-group {
font-size: 16px;
margin-left: 80px;
}
table.google-visualization-table-table {
width: 100%;
}
.reporter-method-name {
font-size: 14px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.reporter-method-output-div {
padding: 5px;
margin: 0 0 5px 20px;
font-size: 12px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
border-width: 0 0 0 1px;
border-style: solid;
}
.ignored-class-div {
font-size: 14px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.ignored-methods-div {
padding: 5px;
margin: 0 0 5px 20px;
font-size: 12px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
border-width: 0 0 0 1px;
border-style: solid;
}
.border-failed {
border-radius:2px;
border-style: solid;
border-width: 0 0 0 10px;
border-color: #F20505;
}
.border-skipped {
border-radius:2px;
border-style: solid;
border-width: 0 0 0 10px;
border-color: #F2BE22;
}
.border-passed {
border-radius:2px;
border-style: solid;
border-width: 0 0 0 10px;
border-color: #038C73;
}
.times-div {
text-align: center;
padding: 5px;
}
.suite-total-time {
font: 16px 'Lucida Grande';
}
.configuration-suite {
margin-left: 20px;
}
.configuration-test {
margin-left: 40px;
}
.configuration-class {
margin-left: 60px;
}
.configuration-method {
margin-left: 80px;
}
.test-method {
margin-left: 100px;
}
.chronological-class {
background-color: #CCD0D1;
border-width: 0 0 1px 1px;/*Chronological*/
}
.method-start {
float: right;
}
.chronological-class-name {
padding: 0 0 0 5px;
margin-top:5px;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #008;
}
.after, .before, .test-method {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
margin-top:5px;
}
.navigator-suite-header {
font-size: 18px;
margin: 0px 10px 10px 5px;
padding: 5px;
border-radius: 10px;
background-color: #027368;
color: white;
font-weight:bold;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
text-align: center; /*All Suites on top of left box*//*Status: -Completed*/
}
.collapse-all-icon {
padding: 3px;
float: right;
}
.button{
position: absolute;
margin-left:500px;
margin-top:8px;
background-color: white;
color:#027368;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight:bold;
border-color:#027368;
border-radius:25px;
cursor: pointer;
height:30px;
width:150px;
outline: none;
}
/*Author: - Akhil Gullapalli*/

View file

@ -0,0 +1,76 @@
window.onload = function () {
let cookies = document.cookie;
let cookieValue = cookies.split('=');
if (cookieValue[1] === 'null' || localStorage.getItem('Theme') === 'null') {
document.getElementById('retro').setAttribute('disabled', 'false');
} else if (cookieValue[1] === 'Switch Ultra Theme' ||
localStorage.getItem('Theme') === 'Switch Ultra Theme') {
document.getElementById('button').innerText = "Switch Retro Theme";
document.getElementById('retro').setAttribute('disabled', 'false');
} else if (cookieValue[1] === 'Switch Retro Theme' ||
localStorage.getItem('Theme') === 'Switch Retro Theme') {
if (cookieValue[1] === 'Switch Ultra Theme' ||
localStorage.getItem('Theme') === 'Switch Ultra Theme') {
document.getElementById('button').innerText = "Switch Retro Theme";
document.getElementById('retro').setAttribute('disabled', 'false');
document.getElementById('button').innerText = "Switch Ultra Theme";
document.getElementById('retro').removeAttribute('disabled');
document.getElementById('ultra').setAttribute('disabled', 'false');
localStorage.setItem('Theme', select);
} else if (select === 'Switch Ultra Theme') {
document.getElementById('button').innerText = "Switch Retro Theme";
document.getElementById('ultra').removeAttribute('disabled');
document.getElementById('retro').setAttribute('disabled', 'false');
localStorage.setItem('Theme', select);
}
} else if (cookieValue[1] === 'Switch Retro Theme' ||
localStorage.getItem('Theme') === 'Switch Retro Theme') {
document.getElementById('button').innerText = "Switch Ultra Theme";
document.getElementById('ultra').setAttribute('disabled', 'false');
}
}
document.getElementById('button').onclick = function () {
let select = document.getElementById('button').innerText;
if (select === 'Switch Retro Theme') {
let d = new Date();
days = 365;
d.setTime(+d + (days * 86400000)); //24 * 60 * 60 * 1000
document.cookie = "Theme =" + select + "; expires=" + d.toGMTString() + ";";
document.getElementById('button').innerText = "Switch Ultra Theme";
document.getElementById('retro').removeAttribute('disabled');
document.getElementById('ultra').setAttribute('disabled', 'false');
localStorage.setItem('Theme', select);
} else if (select === 'Switch Ultra Theme') {
let d = new Date();
days = 365;
d.setTime(+d + (days * 86400000)); //24 * 60 * 60 * 1000
document.cookie = "Theme =" + select + "; expires=" + d.toGMTString() + ";";
document.getElementById('button').innerText = "Switch Retro Theme";
document.getElementById('ultra').removeAttribute('disabled');
document.getElementById('retro').setAttribute('disabled', 'false');
localStorage.setItem('Theme', select);
}
}
//Function to mouse hovering affect.
document.getElementById('button').onmouseover = function () {
document.getElementById('button').style.borderRadius = "25px";
document.getElementById('button').style.width = "180px";
document.getElementById('button').style.height = "45px";
document.getElementById('button').style.marginTop = "1px";
}
//Function to mouse out affect
document.getElementById('button').onmouseout = function () {
document.getElementById('button').style.borderRadius = "25px";
document.getElementById('button').style.width = "150px";
document.getElementById('button').style.height = "30px";
document.getElementById('button').style.marginTop = "8px";
}
//This is the file where we handle the switching of the Themes.
/*Author:- Akhil Gullapalli*/

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<testng-results ignored="0" total="3" passed="3" failed="0" skipped="0">
<reporter-output>
</reporter-output>
<suite started-at="2024-01-18T04:17:28 IST" name="UrlShortenerTestSuite" finished-at="2024-01-18T04:17:29 IST" duration-ms="841">
<groups>
</groups>
<test started-at="2024-01-18T04:17:28 IST" name="UrlShortenerTestSuite" finished-at="2024-01-18T04:17:29 IST" duration-ms="841">
<class name="com.bitmutex.shortener.AnalyticsTest">
<test-method is-config="true" signature="setup()[pri:0, instance:com.bitmutex.shortener.AnalyticsTest@7fc2413d]" started-at="2024-01-18T04:17:29 IST" name="setup" finished-at="2024-01-18T04:17:29 IST" duration-ms="42" status="PASS">
<reporter-output>
</reporter-output>
</test-method> <!-- setup -->
<test-method signature="testAnalyticsEntity()[pri:0, instance:com.bitmutex.shortener.AnalyticsTest@7fc2413d]" started-at="2024-01-18T04:17:29 IST" name="testAnalyticsEntity" finished-at="2024-01-18T04:17:29 IST" duration-ms="0" status="PASS">
<reporter-output>
</reporter-output>
</test-method> <!-- testAnalyticsEntity -->
</class> <!-- com.bitmutex.shortener.AnalyticsTest -->
<class name="com.bitmutex.shortener.AnalyticsControllerTest">
<test-method is-config="true" signature="setup()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@402e37bc]" started-at="2024-01-18T04:17:28 IST" name="setup" finished-at="2024-01-18T04:17:29 IST" duration-ms="751" status="PASS">
<reporter-output>
</reporter-output>
</test-method> <!-- setup -->
<test-method signature="testGetAnalyticsDataWithInvalidShortUrl()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@402e37bc]" started-at="2024-01-18T04:17:29 IST" name="testGetAnalyticsDataWithInvalidShortUrl" finished-at="2024-01-18T04:17:29 IST" duration-ms="21" status="PASS">
<reporter-output>
</reporter-output>
</test-method> <!-- testGetAnalyticsDataWithInvalidShortUrl -->
<test-method signature="testGetAnalyticsDataWithValidShortUrl()[pri:0, instance:com.bitmutex.shortener.AnalyticsControllerTest@402e37bc]" started-at="2024-01-18T04:17:29 IST" name="testGetAnalyticsDataWithValidShortUrl" finished-at="2024-01-18T04:17:29 IST" duration-ms="5" status="PASS">
<reporter-output>
</reporter-output>
</test-method> <!-- testGetAnalyticsDataWithValidShortUrl -->
</class> <!-- com.bitmutex.shortener.AnalyticsControllerTest -->
</test> <!-- UrlShortenerTestSuite -->
</suite> <!-- UrlShortenerTestSuite -->
</testng-results>

9
test-output/testng.css Normal file
View file

@ -0,0 +1,9 @@
.invocation-failed, .test-failed { background-color: #DD0000; }
.invocation-percent, .test-percent { background-color: #006600; }
.invocation-passed, .test-passed { background-color: #00AA00; }
.invocation-skipped, .test-skipped { background-color: #CCCC00; }
.main-page {
font-size: x-large;
}

File diff suppressed because one or more lines are too long