diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml
index dd17a9c..92c82da 100644
--- a/.github/workflows/bld.yml
+++ b/.github/workflows/bld.yml
@@ -1,20 +1,21 @@
name: bld-ci
-on: [ push, pull_request, workflow_dispatch ]
+on: [push, pull_request, workflow_dispatch]
env:
BITLY_ACCESS_TOKEN: ${{ secrets.BITLY_ACCESS_TOKEN }}
COVERAGE_JDK: "21"
- KOTLIN_VERSION: "2.2.0"
+ COVERAGE_KOTLIN: "2.0.0"
+ KOTLIN_HOME: /usr/share/kotlinc
jobs:
build-bld-project:
+ runs-on: ubuntu-latest
+
strategy:
matrix:
- java-version: [ 17, 21, 24 ]
- os: [ ubuntu-latest, windows-latest, macos-latest ]
-
- runs-on: ${{ matrix.os }}
+ java-version: [17, 21, 23]
+ kotlin-version: [1.9.25, 2.1.10]
steps:
- name: Checkout source repository
@@ -22,36 +23,12 @@ jobs:
with:
fetch-depth: 0
- - name: Set up JDK ${{ matrix.java-version }}
+ - name: Set up JDK ${{ matrix.java-version }} with Kotlin ${{ matrix.kotlin-version }}
uses: actions/setup-java@v4
with:
distribution: "zulu"
java-version: ${{ matrix.java-version }}
- - name: Setup Kotlin ${{ env.KOTLIN_VERSION }}
- uses: fwilhe2/setup-kotlin@main
- with:
- version: ${{ env.KOTLIN_VERSION }}
-
- - name: Download dependencies [bld example]
- working-directory: examples/bld
- run: ./bld download
-
- - name: Compile and run examples [bld example]
- working-directory: examples/bld
- run: |
- ./bld compile
- ./bld run --args='https://erik.thauvin.net/ https://bit.ly/2PsNMAA'
- ./bld run-retrieve
- ./bld run-java --args='https://erik.thauvin.net/ https://bit.ly/2PsNMAA'
-
- - name: Run examples [gradle example]
- working-directory: examples/gradle
- run: |
- ./gradlew run --args='https://erik.thauvin.net/ https://bit.ly/2PsNMAA'
- ./gradlew runRetrieve
- ./gradlew runJava --args='https://erik.thauvin.net/ https://bit.ly/2PsNMAA'
-
- name: Download dependencies
run: ./bld download
@@ -62,12 +39,12 @@ jobs:
run: ./bld jacoco
- name: Remove pom.xml
- if: success() && matrix.java-version == env.COVERAGE_JDK && matrix.os == 'ubuntu-latest'
+ if: success() && matrix.java-version == env.COVERAGE_JDK && matrix.kotlin-version == env.COVERAGE_KOTLIN
run: rm -rf pom.xml
- name: SonarCloud Scan
- uses: SonarSource/sonarqube-scan-action@v5.2.0
- if: success() && matrix.java-version == env.COVERAGE_JDK && matrix.os == 'ubuntu-latest'
+ uses: sonarsource/sonarcloud-github-action@master
+ if: success() && matrix.java-version == env.COVERAGE_JDK && matrix.kotlin-version == env.COVERAGE_KOTLIN
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 94f28ea..1e01b48 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -4,6 +4,5 @@
-
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
deleted file mode 100644
index 9fc28bd..0000000
--- a/.idea/kotlinc.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml
index 2b5bd87..c11c5e9 100644
--- a/.idea/libraries/bld.xml
+++ b/.idea/libraries/bld.xml
@@ -2,12 +2,12 @@
-
+
-
+
diff --git a/README.md b/README.md
index 52c5f8b..00001fc 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
[](https://opensource.org/licenses/BSD-3-Clause)
-[](https://kotlinlang.org/)
+[](https://kotlinlang.org/)
[](https://rife2.com/bld)
[](https://github.com/ethauvin/bitly-shorten/releases/latest)
[](https://central.sonatype.com/artifact/net.thauvin.erik/bitly-shorten)
-[](https://github.com/ethauvin/bitly-shorten/packages/2260734/versions)
+[](https://oss.sonatype.org/content/repositories/snapshots/net/thauvin/erik/bitly-shorten/)
[](https://sonarcloud.io/dashboard?id=ethauvin_bitly-shorten)
@@ -60,7 +60,7 @@ BITLY_ACCESS_TOKEN=abc123def456ghi789jkl0
To use with [bld](https://rife2.com/bld), include the following dependency in your [build](https://github.com/ethauvin/bitly-shorten/blob/master/examples/bld/src/bld/java/com/example/ExampleBuild.java) file:
```java
-repositories = List.of(MAVEN_CENTRAL, CENTRAL_SNAPSHOTS);
+repositories = List.of(MAVEN_CENTRAL, SONATYPE_SNAPSHOTS_LEGACY);
scope(compile)
.include(dependency("net.thauvin.erik:bitly-shorten:2.0.0"));
@@ -74,11 +74,8 @@ To use with [Gradle](https://gradle.org/), include the following dependency in y
```gradle
repositories {
- maven {
- name = 'Central Portal Snapshots'
- url = 'https://central.sonatype.com/repository/maven-snapshots/'
- }
mavenCentral()
+ maven { url = uri("https://oss.sonatype.org/content/repositories/snapshots") } // only needed for SNAPSHOT
}
dependencies {
diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml
new file mode 100644
index 0000000..ace99d2
--- /dev/null
+++ b/bitbucket-pipelines.yml
@@ -0,0 +1,20 @@
+image: ubuntu:latest
+
+pipelines:
+ default:
+ - step:
+ name: Test with bld
+ script:
+ # Install latest Java & Kotlin via SDKMAN!
+ - apt-get update -qq && apt-get install -y curl zip
+ - curl -s "https://get.sdkman.io" | bash
+ - echo sdkman_auto_answer=true > $HOME/.sdkman/etc/config
+ - echo sdkman_auto_selfupdate=true >> $HOME/.sdkman/etc/config
+ - source "$HOME/.sdkman/bin/sdkman-init.sh"
+ - sdk install java
+ - sdk install kotlin
+ - source "$HOME/.sdkman/bin/sdkman-init.sh"
+ # Download, compile and test with bld
+ - ./bld download
+ - ./bld compile
+ - ./bld test
diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml
index 717fd7b..dc8efc2 100644
--- a/config/detekt/baseline.xml
+++ b/config/detekt/baseline.xml
@@ -1,6 +1,6 @@
-
+
ConstructorParameterNaming:CreateConfig.kt$CreateConfig.Builder$var long_url: String
FunctionParameterNaming:Bitlinks.kt$Bitlinks$bitlink_id: String
@@ -19,8 +19,8 @@
FunctionParameterNaming:UpdateDeeplinks.kt$UpdateDeeplinks$brand_guid: String
FunctionParameterNaming:UpdateDeeplinks.kt$UpdateDeeplinks$install_type: InstallType
FunctionParameterNaming:UpdateDeeplinks.kt$UpdateDeeplinks$install_url: String
- LongParameterList:Bitlinks.kt$Bitlinks$( bitlink: String, title: String = Constants.EMPTY, archived: Boolean = false, tags: List<String> = emptyList(), deeplinks: UpdateDeeplinks = UpdateDeeplinks(), toJson: Boolean = false )
- LongParameterList:Bitlinks.kt$Bitlinks$( long_url: String, domain: String = Constants.EMPTY, group_guid: String = Constants.EMPTY, title: String = Constants.EMPTY, tags: List<String> = emptyList(), deeplinks: CreateDeeplinks = CreateDeeplinks(), toJson: Boolean = false )
+ LongParameterList:Bitlinks.kt$Bitlinks$( bitlink: String, title: String = Constants.EMPTY, archived: Boolean = false, tags: Array<String> = emptyArray(), deeplinks: UpdateDeeplinks = UpdateDeeplinks(), toJson: Boolean = false )
+ LongParameterList:Bitlinks.kt$Bitlinks$( long_url: String, domain: String = Constants.EMPTY, group_guid: String = Constants.EMPTY, title: String = Constants.EMPTY, tags: Array<String> = emptyArray(), deeplinks: CreateDeeplinks = CreateDeeplinks(), toJson: Boolean = false )
MagicNumber:CallResponse.kt$CallResponse$200
MagicNumber:CallResponse.kt$CallResponse$201
MagicNumber:CallResponse.kt$CallResponse$299
@@ -42,6 +42,7 @@
VariableNaming:CreateConfig.kt$CreateConfig$val group_guid = builder.group_guid
VariableNaming:CreateConfig.kt$CreateConfig$val long_url = builder.long_url
VariableNaming:CreateConfig.kt$CreateConfig.Builder$var group_guid: String = Constants.EMPTY
- WildcardImport:BitlinksTests.kt$import assertk.assertions.*
+ WildcardImport:BitlyTest.kt$import assertk.assertions.*
+ WildcardImport:BitlyTest.kt$import kotlin.test.*
diff --git a/examples/bld/.idea/bld.xml b/examples/bld/.idea/bld.xml
deleted file mode 100644
index 6600cee..0000000
--- a/examples/bld/.idea/bld.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/examples/bld/lib/bld/bld-wrapper.properties b/examples/bld/lib/bld/bld-wrapper.properties
index ec89e30..1ffe3ff 100644
--- a/examples/bld/lib/bld/bld-wrapper.properties
+++ b/examples/bld/lib/bld/bld-wrapper.properties
@@ -1,7 +1,7 @@
bld.downloadExtensionJavadoc=false
bld.downloadExtensionSources=true
bld.downloadLocation=
-bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.1.0
+bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.4
bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES
bld.sourceDirectories=
bld.version=2.2.1
diff --git a/examples/bld/src/bld/java/com/example/ExampleBuild.java b/examples/bld/src/bld/java/com/example/ExampleBuild.java
index 6706974..b23667e 100644
--- a/examples/bld/src/bld/java/com/example/ExampleBuild.java
+++ b/examples/bld/src/bld/java/com/example/ExampleBuild.java
@@ -25,7 +25,7 @@ public class ExampleBuild extends BaseProject {
scope(compile)
.include(dependency("net.thauvin.erik:bitly-shorten:2.0.0"))
- .include(dependency("org.json:json:20250517"));
+ .include(dependency("org.json:json:20250107"));
}
public static void main(String[] args) {
diff --git a/examples/gradle/.idea/kotlinc.xml b/examples/gradle/.idea/kotlinc.xml
index 70661d3..6d0ee1c 100644
--- a/examples/gradle/.idea/kotlinc.xml
+++ b/examples/gradle/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
-
+
\ No newline at end of file
diff --git a/examples/gradle/build.gradle.kts b/examples/gradle/build.gradle.kts
index f153e96..e44fb22 100644
--- a/examples/gradle/build.gradle.kts
+++ b/examples/gradle/build.gradle.kts
@@ -1,7 +1,7 @@
plugins {
id("application")
id("com.github.ben-manes.versions") version "0.51.0"
- kotlin("jvm") version "2.2.0"
+ kotlin("jvm") version "2.1.10"
}
repositories {
diff --git a/examples/gradle/gradle/wrapper/gradle-wrapper.jar b/examples/gradle/gradle/wrapper/gradle-wrapper.jar
index 1b33c55..9bbc975 100644
Binary files a/examples/gradle/gradle/wrapper/gradle-wrapper.jar and b/examples/gradle/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/examples/gradle/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/gradle/wrapper/gradle-wrapper.properties
index ff23a68..37f853b 100644
--- a/examples/gradle/gradle/wrapper/gradle-wrapper.properties
+++ b/examples/gradle/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/examples/gradle/gradlew b/examples/gradle/gradlew
index 23d15a9..faf9300 100755
--- a/examples/gradle/gradlew
+++ b/examples/gradle/gradlew
@@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
-CLASSPATH="\\\"\\\""
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -213,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
- -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
+ org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
diff --git a/examples/gradle/gradlew.bat b/examples/gradle/gradlew.bat
index db3a6ac..9d21a21 100644
--- a/examples/gradle/gradlew.bat
+++ b/examples/gradle/gradlew.bat
@@ -70,11 +70,11 @@ goto fail
:execute
@rem Setup the command line
-set CLASSPATH=
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar
index da7265b..9c4b27e 100644
Binary files a/lib/bld/bld-wrapper.jar and b/lib/bld/bld-wrapper.jar differ
diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties
index 5ac65f0..154abb0 100644
--- a/lib/bld/bld-wrapper.properties
+++ b/lib/bld/bld-wrapper.properties
@@ -1,11 +1,10 @@
bld.downloadExtensionJavadoc=false
bld.downloadExtensionSources=true
bld.downloadLocation=
-bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.10-SNAPSHOT
-bld.extension-dokka=com.uwyn.rife2:bld-dokka:1.0.4-SNAPSHOT
-bld.extension-exec=com.uwyn.rife2:bld-exec:1.0.5
-bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.11-SNAPSHOT
-bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.1.0
-bld.repositories=CENTRAL_SNAPSHOTS,MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES
+bld.extension-detekt=com.uwyn.rife2:bld-detekt:0.9.9
+bld.extension-dokka=com.uwyn.rife2:bld-dokka:1.0.3
+bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.9
+bld.extension-kotlin=com.uwyn.rife2:bld-kotlin:1.0.4
+bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES
bld.sourceDirectories=
-bld.version=2.2.2-SNAPSHOT
+bld.version=2.2.1
diff --git a/pom.xml b/pom.xml
index a05dd59..7dfc770 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
net.thauvin.erik
bitly-shorten
- 2.0.1-SNAPSHOT
+ 2.0.0
bitly-shorten
A simple implementation of the Bitly link shortening API v4
https://github.com/ethauvin/bitly-shorten
@@ -18,37 +18,37 @@
org.jetbrains.kotlin
kotlin-stdlib
- 2.2.0
+ 2.1.10
compile
org.jetbrains.kotlin
kotlin-stdlib-common
- 2.2.0
+ 2.1.10
compile
org.jetbrains.kotlin
kotlin-stdlib-jdk8
- 2.2.0
+ 2.1.10
compile
com.squareup.okhttp3
- okhttp-jvm
- 5.0.0
+ okhttp
+ 4.12.0
compile
com.squareup.okhttp3
logging-interceptor
- 5.0.0
+ 4.12.0
compile
org.json
json
- 20250517
+ 20250107
compile
diff --git a/src/bld/java/net/thauvin/erik/bitly/BitlyShortenBuild.java b/src/bld/java/net/thauvin/erik/bitly/BitlyShortenBuild.java
index 004c31b..d007e1e 100644
--- a/src/bld/java/net/thauvin/erik/bitly/BitlyShortenBuild.java
+++ b/src/bld/java/net/thauvin/erik/bitly/BitlyShortenBuild.java
@@ -33,7 +33,10 @@ package net.thauvin.erik.bitly;
import rife.bld.BuildCommand;
import rife.bld.Project;
-import rife.bld.extension.*;
+import rife.bld.extension.CompileKotlinOperation;
+import rife.bld.extension.DetektOperation;
+import rife.bld.extension.DokkaOperation;
+import rife.bld.extension.JacocoReportOperation;
import rife.bld.extension.dokka.LoggingLevel;
import rife.bld.extension.dokka.OutputFormat;
import rife.bld.extension.dokka.SourceSet;
@@ -46,56 +49,48 @@ import rife.tools.exceptions.FileUtilsErrorException;
import java.io.File;
import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
import java.util.List;
-import java.util.logging.ConsoleHandler;
-import java.util.logging.Level;
-import java.util.logging.Logger;
import static rife.bld.dependencies.Repository.*;
import static rife.bld.dependencies.Scope.compile;
import static rife.bld.dependencies.Scope.test;
public class BitlyShortenBuild extends Project {
- static final String TEST_RESULTS_DIR = "build/test-results/test/";
final File srcMainKotlin = new File(srcMainDirectory(), "kotlin");
public BitlyShortenBuild() {
pkg = "net.thauvin.erik";
name = "bitly-shorten";
- version = version(2, 0, 1, "SNAPSHOT");
+ version = version(2, 0, 0);
javaRelease = 11;
-
downloadSources = true;
autoDownloadPurge = true;
-
repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL);
- var okHttp = version(5, 0, 0);
- final var kotlin = version(2, 2, 0);
+ var okHttp = version(4, 12, 0);
+ final var kotlin = version(2, 1, 10);
scope(compile)
// Kotlin
.include(dependency("org.jetbrains.kotlin", "kotlin-stdlib", kotlin))
.include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-common", kotlin))
.include(dependency("org.jetbrains.kotlin", "kotlin-stdlib-jdk8", kotlin))
// OkHttp
- .include(dependency("com.squareup.okhttp3", "okhttp-jvm", okHttp))
+ .include(dependency("com.squareup.okhttp3", "okhttp", okHttp))
.include(dependency("com.squareup.okhttp3", "logging-interceptor", okHttp))
// JSON
- .include(dependency("org.json", "json", "20250517"));
+ .include(dependency("org.json", "json", "20250107"));
scope(test)
.include(dependency("org.jetbrains.kotlin", "kotlin-test-junit5", kotlin))
- .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 13, 3)))
- .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 13, 3)))
- .include(dependency("org.junit.platform", "junit-platform-launcher", version(1, 13, 3)))
+ .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 1)))
+ .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 1)))
.include(dependency("com.willowtreeapps.assertk", "assertk-jvm", version(0, 28, 1)));
publishOperation()
- .repository(version.isSnapshot() ? repository(CENTRAL_SNAPSHOTS.location())
- .withCredentials(property("central.user"), property("central.password"))
- : repository(CENTRAL_RELEASES.location()))
+ .repository(version.isSnapshot() ? repository(SONATYPE_SNAPSHOTS_LEGACY.location())
+ .withCredentials(property("sonatype.user"), property("sonatype.password"))
+ : repository(SONATYPE_RELEASES_LEGACY.location())
+ .withCredentials(property("sonatype.user"), property("sonatype.password")))
.repository(repository("github"))
.info()
.groupId(pkg)
@@ -124,25 +119,15 @@ public class BitlyShortenBuild extends Project {
}
public static void main(String[] args) {
- // Enable detailed logging for the extensions
- var level = Level.ALL;
- var logger = Logger.getLogger("rife.bld.extension");
- var consoleHandler = new ConsoleHandler();
-
- consoleHandler.setLevel(level);
- logger.addHandler(consoleHandler);
- logger.setLevel(level);
- logger.setUseParentHandlers(false);
-
new BitlyShortenBuild().start(args);
}
@BuildCommand(summary = "Compiles the Kotlin project")
@Override
public void compile() throws Exception {
- var op = new CompileKotlinOperation().fromProject(this);
- op.compileOptions().languageVersion("1.9").verbose(true);
- op.execute();
+ new CompileKotlinOperation()
+ .fromProject(this)
+ .execute();
}
@BuildCommand(summary = "Checks source with Detekt")
@@ -184,60 +169,10 @@ public class BitlyShortenBuild extends Project {
@BuildCommand(summary = "Generates JaCoCo Reports")
public void jacoco() throws Exception {
- var op = new JacocoReportOperation().fromProject(this);
- op.testToolOptions("--reports-dir=" + TEST_RESULTS_DIR);
-
- Exception ex = null;
- try {
- op.execute();
- } catch (Exception e) {
- ex = e;
- }
-
- renderWithXunitViewer();
-
- if (ex != null) {
- throw ex;
- }
- }
-
- @BuildCommand(value = "pom-root", summary = "Generates the POM file in the root directory")
- public void pomRoot() throws FileUtilsErrorException {
- PomBuilder.generateInto(publishOperation().fromProject(this).info(), dependencies(),
- new File(workDirectory, "pom.xml"));
- }
-
- private void renderWithXunitViewer() throws Exception {
- var xunitViewer = new File("/usr/bin/xunit-viewer");
- if (xunitViewer.exists() && xunitViewer.canExecute()) {
- var reportsDir = "build/reports/tests/test/";
-
- Files.createDirectories(Path.of(reportsDir));
-
- new ExecOperation()
- .fromProject(this)
- .command(xunitViewer.getPath(), "-r", TEST_RESULTS_DIR, "-o", reportsDir + "index.html")
- .execute();
- }
- }
-
- @Override
- public void test() throws Exception {
- var op = testOperation().fromProject(this);
- op.testToolOptions().reportsDir(new File(TEST_RESULTS_DIR));
-
- Exception ex = null;
- try {
- op.execute();
- } catch (Exception e) {
- ex = e;
- }
-
- renderWithXunitViewer();
-
- if (ex != null) {
- throw ex;
- }
+ new JacocoReportOperation()
+ .fromProject(this)
+ .sourceFiles(srcMainKotlin)
+ .execute();
}
@Override
@@ -263,4 +198,10 @@ public class BitlyShortenBuild extends Project {
super.publishLocal();
pomRoot();
}
+
+ @BuildCommand(value = "pom-root", summary = "Generates the POM file in the root directory")
+ public void pomRoot() throws FileUtilsErrorException {
+ PomBuilder.generateInto(publishOperation().fromProject(this).info(), dependencies(),
+ new File(workDirectory, "pom.xml"));
+ }
}
diff --git a/src/main/kotlin/net/thauvin/erik/bitly/Bitlinks.kt b/src/main/kotlin/net/thauvin/erik/bitly/Bitlinks.kt
index f7cb3a4..78ec984 100644
--- a/src/main/kotlin/net/thauvin/erik/bitly/Bitlinks.kt
+++ b/src/main/kotlin/net/thauvin/erik/bitly/Bitlinks.kt
@@ -136,7 +136,7 @@ open class Bitlinks(private val accessToken: String) {
domain: String = Constants.EMPTY,
group_guid: String = Constants.EMPTY,
title: String = Constants.EMPTY,
- tags: List = emptyList(),
+ tags: Array = emptyArray(),
deeplinks: CreateDeeplinks = CreateDeeplinks(),
toJson: Boolean = false
): String {
@@ -282,7 +282,7 @@ open class Bitlinks(private val accessToken: String) {
bitlink: String,
title: String = Constants.EMPTY,
archived: Boolean = false,
- tags: List = emptyList(),
+ tags: Array = emptyArray(),
deeplinks: UpdateDeeplinks = UpdateDeeplinks(),
toJson: Boolean = false
): String {
diff --git a/src/main/kotlin/net/thauvin/erik/bitly/Utils.kt b/src/main/kotlin/net/thauvin/erik/bitly/Utils.kt
index 956a640..17441e0 100644
--- a/src/main/kotlin/net/thauvin/erik/bitly/Utils.kt
+++ b/src/main/kotlin/net/thauvin/erik/bitly/Utils.kt
@@ -124,7 +124,7 @@ object Utils {
var message = response.message
var description = ""
var json = Constants.EMPTY_JSON
- response.body.string().let { body ->
+ response.body?.string()?.let { body ->
json = body
if (!response.isSuccessful && body.isNotEmpty()) {
try {
@@ -174,9 +174,8 @@ object Utils {
return false
}
-
/**
- * Removes the `http` or `https` schemes from a string.
+ * Removes http(s) scheme from string.
*/
@JvmStatic
fun String.removeHttp(): String {
@@ -184,7 +183,7 @@ object Utils {
}
/**
- * Converts a path to an API endpoint URL using the [Constants.API_BASE_URL], unless a URL is already specified.
+ * Builds the full API endpoint URL using the [Constants.API_BASE_URL].
*/
@JvmStatic
fun String.toEndPoint(): String {
diff --git a/src/main/kotlin/net/thauvin/erik/bitly/config/CreateConfig.kt b/src/main/kotlin/net/thauvin/erik/bitly/config/CreateConfig.kt
index 928cee1..25503b7 100644
--- a/src/main/kotlin/net/thauvin/erik/bitly/config/CreateConfig.kt
+++ b/src/main/kotlin/net/thauvin/erik/bitly/config/CreateConfig.kt
@@ -60,7 +60,7 @@ class CreateConfig private constructor(builder: Builder) {
var domain: String = Constants.EMPTY
var group_guid: String = Constants.EMPTY
var title: String = Constants.EMPTY
- var tags: List = emptyList()
+ var tags: Array = emptyArray()
var deeplinks: CreateDeeplinks = CreateDeeplinks()
var toJson: Boolean = false
@@ -76,7 +76,7 @@ class CreateConfig private constructor(builder: Builder) {
fun title(title: String): Builder = apply { this.title = title }
- fun tags(tags: List): Builder = apply { this.tags = tags }
+ fun tags(tags: Array): Builder = apply { this.tags = tags }
fun deeplinks(deeplinks: CreateDeeplinks): Builder = apply { this.deeplinks = deeplinks }
diff --git a/src/main/kotlin/net/thauvin/erik/bitly/config/UpdateConfig.kt b/src/main/kotlin/net/thauvin/erik/bitly/config/UpdateConfig.kt
index 52ba5ab..d531d1b 100644
--- a/src/main/kotlin/net/thauvin/erik/bitly/config/UpdateConfig.kt
+++ b/src/main/kotlin/net/thauvin/erik/bitly/config/UpdateConfig.kt
@@ -57,7 +57,7 @@ class UpdateConfig private constructor(builder: Builder) {
data class Builder(var bitlink: String) {
var title: String = Constants.EMPTY
var archived: Boolean = false
- var tags: List = emptyList()
+ var tags: Array = emptyArray()
var deeplinks: UpdateDeeplinks = UpdateDeeplinks()
var toJson: Boolean = false
@@ -68,7 +68,7 @@ class UpdateConfig private constructor(builder: Builder) {
fun title(title: String): Builder = apply { this.title = title }
fun archived(archived: Boolean): Builder = apply { this.archived = archived }
- fun tags(tags: List): Builder = apply { this.tags = tags }
+ fun tags(tags: Array): Builder = apply { this.tags = tags }
fun deeplinks(deeplinks: UpdateDeeplinks): Builder = apply { this.deeplinks = deeplinks }
/**
diff --git a/src/test/kotlin/net/thauvin/erik/bitly/BeforeAllTests.kt b/src/test/kotlin/net/thauvin/erik/bitly/BeforeAllTests.kt
deleted file mode 100644
index 06e21ab..0000000
--- a/src/test/kotlin/net/thauvin/erik/bitly/BeforeAllTests.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * BeforeAllTests.kt
- *
- * Copyright 2020-2025 Erik C. Thauvin (erik@thauvin.net)
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * Neither the name of this project nor the names of its contributors may be
- * used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package net.thauvin.erik.bitly
-
-import org.junit.jupiter.api.extension.BeforeAllCallback
-import org.junit.jupiter.api.extension.ExtensionContext
-import java.util.concurrent.atomic.AtomicBoolean
-import java.util.logging.ConsoleHandler
-import java.util.logging.Level
-
-class BeforeAllTests : BeforeAllCallback {
- private val isFirstTime: AtomicBoolean = AtomicBoolean(true)
-
- override fun beforeAll(context: ExtensionContext?) {
- if (isFirstTime.getAndSet(false)) {
- with(Utils.logger) {
- addHandler(ConsoleHandler().apply { level = Level.FINE })
- level = Level.FINE
- }
- }
- }
-}
-
diff --git a/src/test/kotlin/net/thauvin/erik/bitly/BitlinksTests.kt b/src/test/kotlin/net/thauvin/erik/bitly/BitlinksTests.kt
deleted file mode 100644
index b248de0..0000000
--- a/src/test/kotlin/net/thauvin/erik/bitly/BitlinksTests.kt
+++ /dev/null
@@ -1,417 +0,0 @@
-/*
- * BitlinksTests.kt
- *
- * Copyright 2020-2025 Erik C. Thauvin (erik@thauvin.net)
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * Neither the name of this project nor the names of its contributors may be
- * used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package net.thauvin.erik.bitly
-
-import DisableOnCi
-import assertk.all
-import assertk.assertThat
-import assertk.assertions.*
-import net.thauvin.erik.bitly.config.CreateConfig
-import net.thauvin.erik.bitly.config.UpdateConfig
-import net.thauvin.erik.bitly.config.deeplinks.CreateDeeplinks
-import net.thauvin.erik.bitly.config.deeplinks.UpdateDeeplinks
-import net.thauvin.erik.bitly.config.deeplinks.enums.InstallType
-import net.thauvin.erik.bitly.config.deeplinks.enums.Os
-import org.junit.jupiter.api.DisplayName
-import org.junit.jupiter.api.Nested
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable
-import org.junit.jupiter.api.extension.ExtendWith
-import java.io.File
-import java.util.*
-import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
-import kotlin.test.assertTrue
-
-
-@ExtendWith(BeforeAllTests::class)
-class BitlinksTests {
- private val bitly = with(File("local.properties")) {
- if (exists()) {
- Bitly(toPath())
- } else {
- Bitly()
- }
- }
- private val longUrl = "https://erik.thauvin.net/blog"
- private val shortUrl = "https://bit.ly/380ojFd"
-
- @Nested
- @DisplayName("Bitlinks Tests")
- inner class BitlinksTests {
- @Test
- fun `Clicks summary`() {
- val bl = bitly.bitlinks()
- val clicks = bl.clicks(shortUrl, unit = Units.MONTH, units = 1)
- assertThat(bl.lastCallResponse).all {
- prop(CallResponse::description).isEmpty()
- prop(CallResponse::isSuccessful).isTrue()
- prop(CallResponse::statusCode).isEqualTo(200)
- }
- assertThat(clicks.toInt()).isGreaterThanOrEqualTo(0)
- }
-
- @Test
- fun `Clicks summary as json`() {
- val bl = bitly.bitlinks()
- val clicks = bl.clicks(shortUrl, toJson = true)
- assertThat(bl.lastCallResponse).all {
- prop(CallResponse::description).isEmpty()
- prop(CallResponse::isSuccessful).isTrue()
- prop(CallResponse::statusCode).isEqualTo(200)
- }
- assertThat(clicks).startsWith("{\"unit_reference\":")
- }
- }
-
- @Nested
- @DisplayName("Constructor Tests")
- inner class ConstructorTests {
- @Test
- fun `Constructor with access token string should set the token correctly`() {
- val expectedToken = "my-secret-token"
-
- val bitly = Bitly(expectedToken)
-
- assertThat(bitly.accessToken).isEqualTo(expectedToken)
- }
-
- @Test
- fun `Constructor with Path should not change token if file does not exist`() {
- val nonExistentPath = File("non/existent/path/file.properties").toPath()
- val bitlyWithDefaultToken = Bitly()
- val initialToken = bitlyWithDefaultToken.accessToken
-
- val bitly = Bitly(nonExistentPath)
-
- assertThat(bitly.accessToken).isEqualTo(initialToken)
- }
-
- @Test
- fun `Constructor with Properties and custom key should set token correctly`() {
- val customKey = "MY_CUSTOM_BITLY_KEY"
- val expectedToken = "token-from-custom-key"
- val properties = Properties().apply {
- setProperty(customKey, expectedToken)
- }
-
- val bitly = Bitly(properties, customKey)
-
- assertThat(bitly.accessToken).isEqualTo(expectedToken)
- }
-
- @Test
- fun `Constructor with Properties should keep default token if key not found`() {
- val properties = Properties() // Empty properties
- val bitlyWithDefaultToken = Bitly() // Has "" as token by default
- val initialToken = bitlyWithDefaultToken.accessToken
-
- val bitly = Bitly(properties, "non-existent-key")
-
- // The token should remain the default empty string
- assertThat(bitly.accessToken).isEqualTo(initialToken)
- }
-
- @Test
- fun `Constructor with Properties should set token using default key`() {
- val expectedToken = "token-from-props"
- val properties = Properties().apply {
- setProperty(Constants.ENV_ACCESS_TOKEN, expectedToken)
- }
-
- val bitly = Bitly(properties)
-
- assertThat(bitly.accessToken).isEqualTo(expectedToken)
- }
-
- @Test
- @DisableOnCi
- fun `Default constructor should default to empty string if no token is provided`() {
- System.clearProperty(Constants.ENV_ACCESS_TOKEN)
-
- val bitly = Bitly()
-
- assertThat(bitly.accessToken).isEmpty()
- }
-
- @Test
- @DisableOnCi
- fun `Default constructor should use system property if env var is not set`() {
- val expectedToken = "token-from-property"
- System.setProperty(Constants.ENV_ACCESS_TOKEN, expectedToken)
-
- val bitly = Bitly()
-
- assertThat(bitly.accessToken).isEqualTo(expectedToken)
- }
- }
-
- @Nested
- @DisplayName("Create Bitlink Tests")
- inner class CreateBitlinkTests {
-
- @Test
- fun `Create bitlink`() {
- assertThat(bitly.bitlinks().create(long_url = longUrl), "create(longUrl)")
- .matches("https://\\w+.\\w{2}/\\w{7}".toRegex())
- assertEquals(
- shortUrl,
- bitly.bitlinks().create(
- domain = "bit.ly",
- title = "Erik's Blog",
- tags = listOf("erik", "thauvin", "blog", "weblog"),
- long_url = longUrl
- )
- )
- }
-
- @Test
- fun `Create bitlink with config`() {
- var config = CreateConfig.Builder(longUrl).build()
- assertThat(bitly.bitlinks().create(config), "create(config)")
- .matches("https://\\w+.\\w{2}/\\w{7}".toRegex())
-
- config = CreateConfig.Builder(longUrl)
- .domain("bit.ly")
- .title("Erik's Blog")
- .tags(listOf("erik", "thauvin", "blog", "weblog"))
- .build()
- assertEquals(
- shortUrl,
- bitly.bitlinks().create(config)
- )
- }
-
- @Test
- fun `Create bitlink with deep links`() {
- val bl = bitly.bitlinks()
- val dl = CreateDeeplinks().apply {
- app_uri_path("/store?id=123456")
- install_type(InstallType.NO_INSTALL)
- install_url("https://play.google.com/store/apps/details?id=com.bitly.app&hl=en_US")
- }
-
- val config = CreateConfig.Builder(longUrl)
- .deeplinks(dl)
- .domain("bit.ly")
- .build()
-
- assertThat(bl.create(config)).isEqualTo(Constants.EMPTY)
- assertThat(bl.lastCallResponse.isUpgradeRequired).isTrue()
- }
- }
-
- @Nested
- @DisplayName("Expand Test")
- inner class ExpandTests {
-
- @Test
- fun `Expand as json`() {
- assertTrue(
- bitly.bitlinks().expand(shortUrl, toJson = true)
- .startsWith("{\"created_at\":")
- )
- }
-
- @Test
- fun `Expand link`() {
- assertEquals(longUrl, Bitlinks(bitly.accessToken).expand(shortUrl))
- }
- }
-
- @Nested
- @DisplayName("Shorten Tests")
- inner class ShortenTests {
-
- @Test
- fun `Shorten as json`() {
- assertTrue(
- bitly.bitlinks().shorten(longUrl, toJson = true)
- .startsWith("{\"created_at\":")
- )
- }
-
- @Test
- fun `Shorten last call response`() {
- val bl = Bitlinks(bitly.accessToken)
- bl.shorten(longUrl, domain = "bit.ly")
- assertThat(bl.lastCallResponse, "shorten(longUrl)").all {
- prop(CallResponse::body).contains("\"link\":\"$shortUrl\"")
- prop(CallResponse::isSuccessful).isTrue()
- prop(CallResponse::message).isEmpty()
- prop(CallResponse::statusCode).isEqualTo(200)
- }
-
- bl.shorten(shortUrl)
- assertThat(bl.lastCallResponse, "shorten(shortUrl)").all {
- prop(CallResponse::description).isEqualTo("The value provided is invalid.")
- prop(CallResponse::isBadRequest).isTrue()
- prop(CallResponse::isSuccessful).isFalse()
- prop(CallResponse::message).isEqualTo("ALREADY_A_BITLY_LINK")
- prop(CallResponse::statusCode).isEqualTo(400)
- }
- }
-
- @Test
- fun `Shorten link`() {
- assertEquals(
- shortUrl, Bitlinks(bitly.accessToken)
- .shorten(longUrl, domain = "bit.ly")
- )
- }
-
- @Test
- fun `Shorten with invalid domain`() {
- val bl = bitly.bitlinks()
- bl.shorten("https://www.examples.com", domain = "foo.com")
- assertThat(bl.lastCallResponse).all {
- prop(CallResponse::description).contains("invalid")
- prop(CallResponse::isBadRequest).isTrue()
- prop(CallResponse::isSuccessful).isFalse()
- prop(CallResponse::message).isEqualTo("INVALID_ARG_DOMAIN")
- }
- }
- }
-
- @Nested
- @DisplayName("Update Bitlink Tests")
- inner class UpdateBitlinkTests {
-
- @Test
- fun `Update bitlink`() {
- val bl = bitly.bitlinks()
- assertEquals(
- Constants.TRUE,
- bl.update(
- shortUrl, title = "Erik's Weblog", tags = listOf("blog", "weblog"), archived = true
- )
- )
-
- assertThat(bl.update(shortUrl, tags = emptyList(), toJson = true), "update(tags)")
- .contains("\"tags\":[]")
- }
-
- @Test
- fun `Update bitlink with config`() {
- val bl = bitly.bitlinks()
- var config = UpdateConfig.Builder(shortUrl)
- .archived(true)
- .tags(listOf("blog", "weblog"))
- .title("Erik's Weblog")
- .build()
-
- assertEquals(Constants.TRUE, bl.update(config))
-
- config = UpdateConfig.Builder(shortUrl)
- .toJson(true)
- .build()
-
- assertThat(bl.update(config), "update(tags)").contains("\"tags\":[]")
- }
-
- @Test
- fun `Update bitlink with deep links`() {
- val bl = bitly.bitlinks()
- val dl = UpdateDeeplinks().apply {
- os(Os.ANDROID)
- brand_guid("Ba1bc23dE4F")
- }
- val config = UpdateConfig.Builder(shortUrl)
- .deeplinks(dl)
- .build()
-
- assertThat(bl.update(config)).isEqualTo(Constants.FALSE)
- assertThat(bl.lastCallResponse.isUpgradeRequired).isTrue()
- }
-
- @Test
- @DisableOnCi
- fun `Update custom bitlink`() {
- val bl = bitly.bitlinks()
- assertEquals(
- Constants.TRUE,
- bl.updateCustom("https://thauv.in/2NwtljT", "thauv.in/2NwtljT")
- )
- }
- }
-
- @Nested
- @DisplayName("Validation Tests")
- inner class ValidationTests {
-
- @Test
- fun `Empty URL should not shorten`() {
- assertEquals(Constants.EMPTY, bitly.bitlinks().shorten(Constants.EMPTY))
- }
-
- @Test
- fun `Short URL should not shorten`() {
- assertEquals(shortUrl, bitly.bitlinks().shorten(shortUrl))
- }
-
- @Test
- @DisableOnCi
- fun `Token not specified`() {
- val test = Bitly()
-
- assertFailsWith(IllegalArgumentException::class) {
- test.bitlinks().shorten(longUrl)
- }
- }
-
- @Test
- @EnabledIfEnvironmentVariable(named = "CI", matches = "true")
- fun `Token not specified on CI`() {
- val test = Bitly(Constants.EMPTY) // to avoid picking up the environment variable
-
- assertFailsWith(IllegalArgumentException::class) {
- test.bitlinks().shorten(longUrl)
- }
- }
-
- @Test
- fun `Token not specified with API call`() {
- assertFailsWith(IllegalArgumentException::class, "Utils.call()") {
- Utils.call("", "foo")
- }
- }
-
- @Test
- fun `Token should be valid`() {
- val test = Bitly().apply { accessToken = "12345679" }
- assertEquals(
- "{\"message\":\"FORBIDDEN\"}",
- test.bitlinks().shorten("https://erik.thauvin.net/blog", toJson = true)
- )
- }
- }
-}
diff --git a/src/test/kotlin/net/thauvin/erik/bitly/BitlyTest.kt b/src/test/kotlin/net/thauvin/erik/bitly/BitlyTest.kt
new file mode 100644
index 0000000..2af5f86
--- /dev/null
+++ b/src/test/kotlin/net/thauvin/erik/bitly/BitlyTest.kt
@@ -0,0 +1,324 @@
+/*
+ * BitlyTest.kt
+ *
+ * Copyright 2020-2025 Erik C. Thauvin (erik@thauvin.net)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of this project nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.thauvin.erik.bitly
+
+import DisableOnCi
+import assertk.all
+import assertk.assertThat
+import assertk.assertions.*
+import net.thauvin.erik.bitly.Utils.isValidUrl
+import net.thauvin.erik.bitly.Utils.removeHttp
+import net.thauvin.erik.bitly.Utils.toEndPoint
+import net.thauvin.erik.bitly.config.CreateConfig
+import net.thauvin.erik.bitly.config.UpdateConfig
+import net.thauvin.erik.bitly.config.deeplinks.CreateDeeplinks
+import net.thauvin.erik.bitly.config.deeplinks.UpdateDeeplinks
+import net.thauvin.erik.bitly.config.deeplinks.enums.InstallType
+import net.thauvin.erik.bitly.config.deeplinks.enums.Os
+import org.json.JSONObject
+import org.junit.jupiter.api.BeforeAll
+import java.io.File
+import java.util.logging.Level
+import kotlin.test.*
+
+class BitlyTest {
+ private val bitly = with(File("local.properties")) {
+ if (exists()) {
+ Bitly(toPath())
+ } else {
+ Bitly()
+ }
+ }
+ private val longUrl = "https://erik.thauvin.net/blog"
+ private val shortUrl = "https://bit.ly/380ojFd"
+
+ @Test
+ fun `token should be specified`() {
+ val test = Bitly()
+ if (System.getenv("CI") == "true") {
+ test.accessToken = Constants.EMPTY
+ }
+ assertFailsWith(IllegalArgumentException::class) {
+ test.bitlinks().shorten(longUrl)
+ }
+ assertFailsWith(IllegalArgumentException::class, "Utils.call()") {
+ Utils.call("", "foo")
+ }
+ }
+
+ @Test
+ fun `token should be valid`() {
+ val test = Bitly().apply { accessToken = "12345679" }
+ assertEquals(
+ "{\"message\":\"FORBIDDEN\"}",
+ test.bitlinks().shorten("https://erik.thauvin.net/blog", toJson = true)
+ )
+ }
+
+ @Test
+ fun `long url should be valid`() {
+ assertEquals(Constants.EMPTY, bitly.bitlinks().shorten(Constants.EMPTY))
+ }
+
+ @Test
+ fun `long url should not be short`() {
+ assertEquals(shortUrl, bitly.bitlinks().shorten(shortUrl))
+ }
+
+ @Test
+ fun `endPoint should be specified`() {
+ assertFailsWith(IllegalArgumentException::class, "bitly.call()") {
+ bitly.call("")
+ }
+ assertFailsWith(IllegalArgumentException::class, "Utils.call()") {
+ Utils.call("1234568", "")
+ }
+ }
+
+ @Test
+ fun `endPoint conversion`() {
+ assertThat(Constants.API_BASE_URL.toEndPoint()).isEqualTo(Constants.API_BASE_URL)
+ assertThat("path".toEndPoint()).isEqualTo("${Constants.API_BASE_URL}/path")
+ assertThat("/path".toEndPoint()).isEqualTo("${Constants.API_BASE_URL}/path")
+ }
+
+ @Test
+ fun `shorten = expand`() {
+ val shortUrl = bitly.bitlinks().shorten(longUrl, domain = "bit.ly")
+ assertEquals(longUrl, bitly.bitlinks().expand(shortUrl))
+ }
+
+ @Test
+ fun `shorten as json`() {
+ assertTrue(bitly.bitlinks().shorten(longUrl, toJson = true).startsWith("{\"created_at\":"))
+ }
+
+ @Test
+ fun `get user`() {
+ assertThat(bitly.call("user", method = Methods.GET), "call(user)")
+ .prop(CallResponse::isSuccessful).isTrue()
+ assertThat(Utils.call(bitly.accessToken, "user".toEndPoint(), method = Methods.GET), "call(/user)").all {
+ prop(CallResponse::isSuccessful).isTrue()
+ prop(CallResponse::body).contains("login")
+ }
+ }
+
+ @Test
+ fun `created by`() {
+ assertEquals(
+ "ethauvin",
+ JSONObject(
+ bitly.call(
+ "/bitlinks/${shortUrl.removeHttp()}",
+ method = Methods.GET
+ ).body
+ ).getString("created_by")
+ )
+ }
+
+ @Test
+ fun `bitlinks shorten`() {
+ assertEquals(shortUrl, Bitlinks(bitly.accessToken).shorten(longUrl, domain = "bit.ly"))
+ }
+
+ @Test
+ fun `bitlinks expand`() {
+ assertEquals(longUrl, Bitlinks(bitly.accessToken).expand(shortUrl))
+ }
+
+ @Test
+ fun `bitlinks lastCallResponse`() {
+ val bl = Bitlinks(bitly.accessToken)
+ bl.shorten(longUrl, domain = "bit.ly")
+ assertThat(bl.lastCallResponse, "shorten(longUrl)").all {
+ prop(CallResponse::isSuccessful).isTrue()
+ prop(CallResponse::statusCode).isEqualTo(200)
+ prop(CallResponse::body).contains("\"link\":\"$shortUrl\"")
+ prop(CallResponse::message).isEmpty()
+ }
+
+ bl.shorten(shortUrl)
+ assertThat(bl.lastCallResponse, "shorten(shortUrl)").all {
+ prop(CallResponse::isSuccessful).isFalse()
+ prop(CallResponse::statusCode).isEqualTo(400)
+ prop(CallResponse::isBadRequest).isTrue()
+ prop(CallResponse::message).isEqualTo("ALREADY_A_BITLY_LINK")
+ prop(CallResponse::description).isEqualTo("The value provided is invalid.")
+ }
+ }
+
+ @Test
+ fun `clicks summary`() {
+ val bl = bitly.bitlinks()
+ assertThat(bl.clicks(shortUrl)).isNotEqualTo(Constants.EMPTY)
+ val clicks = bl.clicks(shortUrl, unit = Units.MONTH, units = 1)
+ assertThat(bl.lastCallResponse).all {
+ prop(CallResponse::isSuccessful).isTrue()
+ prop(CallResponse::statusCode).isEqualTo(200)
+ prop(CallResponse::description).isEmpty()
+ }
+ assertThat(clicks.toInt()).isGreaterThanOrEqualTo(0)
+ }
+
+ @Test
+ fun `create bitlink`() {
+ assertThat(bitly.bitlinks().create(long_url = longUrl), "create(longUrl)")
+ .matches("https://\\w+.\\w{2}/\\w{7}".toRegex())
+ assertEquals(
+ shortUrl,
+ bitly.bitlinks().create(
+ domain = "bit.ly",
+ title = "Erik's Blog",
+ tags = arrayOf("erik", "thauvin", "blog", "weblog"),
+ long_url = longUrl
+ )
+ )
+ }
+
+ @Test
+ fun `create bitlink with config`() {
+ var config = CreateConfig.Builder(longUrl).build()
+ assertThat(bitly.bitlinks().create(config), "create(config)")
+ .matches("https://\\w+.\\w{2}/\\w{7}".toRegex())
+
+ config = CreateConfig.Builder(longUrl)
+ .domain("bit.ly")
+ .title("Erik's Blog")
+ .tags(arrayOf("erik", "thauvin", "blog", "weblog"))
+ .build()
+ assertEquals(
+ shortUrl,
+ bitly.bitlinks().create(config)
+ )
+ }
+
+ @Test
+ fun `create bitlink with deeplinks`() {
+ val bl = bitly.bitlinks()
+ val dl = CreateDeeplinks().apply {
+ install_type(InstallType.NO_INSTALL)
+ app_uri_path("/store?id=123456")
+ install_url("https://play.google.com/store/apps/details?id=com.bitly.app&hl=en_US")
+ }
+
+ val config = CreateConfig.Builder(longUrl)
+ .domain("bit.ly")
+ .deeplinks(dl)
+ .build()
+
+ assertThat(bl.create(config)).isEqualTo(Constants.EMPTY)
+ assertThat(bl.lastCallResponse.isUpgradeRequired).isTrue()
+ }
+
+ @Test
+ fun `shorten with invalid domain`() {
+ val bl = bitly.bitlinks()
+ bl.shorten("https://www.examples.com", domain = "foo.com")
+ assertThat(bl.lastCallResponse).all {
+ prop(CallResponse::isSuccessful).isFalse()
+ prop(CallResponse::isBadRequest).isTrue()
+ prop(CallResponse::message).isEqualTo("INVALID_ARG_DOMAIN")
+ prop(CallResponse::description).contains("invalid")
+ }
+ }
+
+ @Test
+ @DisableOnCi
+ fun `update custom bitlink`() {
+ val bl = bitly.bitlinks()
+ assertEquals(
+ Constants.TRUE,
+ bl.updateCustom("https://thauv.in/2NwtljT", "thauv.in/2NwtljT")
+ )
+ }
+
+ @Test
+ fun `update bitlink`() {
+ val bl = bitly.bitlinks()
+ assertEquals(
+ Constants.TRUE,
+ bl.update(shortUrl, title = "Erik's Weblog", tags = arrayOf("blog", "weblog"), archived = true)
+ )
+
+ assertThat(bl.update(shortUrl, tags = emptyArray(), toJson = true), "update(tags)")
+ .contains("\"tags\":[]")
+ }
+
+ @Test
+ fun `update bitlink with config`() {
+ val bl = bitly.bitlinks()
+ var config = UpdateConfig.Builder(shortUrl)
+ .title("Erik's Weblog")
+ .tags(arrayOf("blog", "weblog"))
+ .archived(true)
+ .build()
+
+ assertEquals(Constants.TRUE, bl.update(config))
+
+ config = UpdateConfig.Builder(shortUrl)
+ .toJson(true)
+ .build()
+
+ assertThat(bl.update(config), "update(tags)").contains("\"tags\":[]")
+ }
+
+ @Test
+ fun `update bitlink with deeplinks`() {
+ val bl = bitly.bitlinks()
+ val dl = UpdateDeeplinks().apply {
+ os(Os.ANDROID)
+ brand_guid("Ba1bc23dE4F")
+ }
+ val config = UpdateConfig.Builder(shortUrl)
+ .deeplinks(dl)
+ .build()
+
+ assertThat(bl.update(config)).isEqualTo(Constants.FALSE)
+ assertThat(bl.lastCallResponse.isUpgradeRequired).isTrue()
+ }
+
+ @Test
+ fun `validate URL`() {
+ assertTrue("https://www.example.com".isValidUrl(), "valid url")
+ assertFalse("this is a test".isValidUrl(), "invalid url")
+ }
+
+ companion object {
+ @JvmStatic
+ @BeforeAll
+ fun before() {
+ with(Utils.logger) {
+ level = Level.FINE
+ }
+ }
+ }
+}
diff --git a/src/test/kotlin/net/thauvin/erik/bitly/BitlyTests.kt b/src/test/kotlin/net/thauvin/erik/bitly/BitlyTests.kt
deleted file mode 100644
index afd24e5..0000000
--- a/src/test/kotlin/net/thauvin/erik/bitly/BitlyTests.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * BitlyTests.kt
- *
- * Copyright 2020-2025 Erik C. Thauvin (erik@thauvin.net)
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * Neither the name of this project nor the names of its contributors may be
- * used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package net.thauvin.erik.bitly
-
-import assertk.all
-import assertk.assertThat
-import assertk.assertions.contains
-import assertk.assertions.isTrue
-import assertk.assertions.prop
-import net.thauvin.erik.bitly.Utils.removeHttp
-import net.thauvin.erik.bitly.Utils.toEndPoint
-import org.json.JSONObject
-import org.junit.jupiter.api.DisplayName
-import org.junit.jupiter.api.Nested
-import org.junit.jupiter.api.extension.ExtendWith
-import java.io.File
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
-
-@ExtendWith(BeforeAllTests::class)
-class BitlyTests {
- private val bitly = with(File("local.properties")) {
- if (exists()) {
- Bitly(toPath())
- } else {
- Bitly()
- }
- }
- private val shortUrl = "https://bit.ly/380ojFd"
-
- @Nested
- @DisplayName("API Call Tests")
- inner class ApiCallTests {
- @Test
- fun `Created by`() {
- assertEquals(
- "ethauvin",
- JSONObject(
- bitly.call(
- "/bitlinks/${shortUrl.removeHttp()}",
- method = Methods.GET
- ).body
- ).getString("created_by")
- )
- }
-
- @Test
- fun `EndPoint should be specified`() {
- assertFailsWith(IllegalArgumentException::class, "bitly.call()") {
- bitly.call("")
- }
- assertFailsWith(IllegalArgumentException::class, "Utils.call()") {
- Utils.call("1234568", "")
- }
- }
-
- @Test
- fun `Get user`() {
- assertThat(bitly.call("user", method = Methods.GET), "call(user)")
- .prop(CallResponse::isSuccessful).isTrue()
- assertThat(
- Utils.call(
- bitly.accessToken, "user".toEndPoint(), method = Methods.GET
- ), "call(/user)"
- ).all {
- prop(CallResponse::isSuccessful).isTrue()
- prop(CallResponse::body).contains("login")
- }
- }
- }
-}
diff --git a/src/test/kotlin/net/thauvin/erik/bitly/UtilsTests.kt b/src/test/kotlin/net/thauvin/erik/bitly/UtilsTests.kt
deleted file mode 100644
index 66f31e5..0000000
--- a/src/test/kotlin/net/thauvin/erik/bitly/UtilsTests.kt
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * UtilsTests.kt
- *
- * Copyright 2020-2025 Erik C. Thauvin (erik@thauvin.net)
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * Neither the name of this project nor the names of its contributors may be
- * used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package net.thauvin.erik.bitly
-
-import assertk.assertThat
-import assertk.assertions.isEqualTo
-import net.thauvin.erik.bitly.Utils.isValidUrl
-import net.thauvin.erik.bitly.Utils.removeHttp
-import net.thauvin.erik.bitly.Utils.toEndPoint
-import org.junit.jupiter.api.DisplayName
-import org.junit.jupiter.api.Nested
-import org.junit.jupiter.api.Test
-import kotlin.test.assertFalse
-import kotlin.test.assertTrue
-
-class UtilsTests {
- @Nested
- @DisplayName("Endpoint Conversion Tests")
- inner class EndpointConversionTests {
- @Test
- fun `Convert endpoint with empty string`() {
- assertThat("".toEndPoint()).isEqualTo("")
- }
-
- @Test
- fun `Convert endpoint with blank string`() {
- assertThat(" ".toEndPoint()).isEqualTo(" ")
- }
-
- @Test
- fun `Convert endpoint with full URL`() {
- assertThat("https://example.com/path".toEndPoint()).isEqualTo("https://example.com/path")
- }
-
- @Test
- fun `Convert endpoint with leading slash`() {
- assertThat("/path".toEndPoint()).isEqualTo("${Constants.API_BASE_URL}/path")
- }
-
- @Test
- fun `Convert endpoint with multiple path segments`() {
- assertThat("/existing/path".toEndPoint()).isEqualTo("${Constants.API_BASE_URL}/existing/path")
- }
-
- @Test
- fun `Convert endpoint with no leading slash`() {
- assertThat("path".toEndPoint()).isEqualTo("${Constants.API_BASE_URL}/path")
- }
-
- @Test
- fun `Convert endpoint with trailing slash`() {
- assertThat("path/".toEndPoint()).isEqualTo("${Constants.API_BASE_URL}/path/")
- }
- }
-
- @Nested
- @DisplayName("Remote HTTP Tests")
- inner class RemoteHTTPTests {
- @Test
- @Suppress("HttpUrlsUsage")
- fun `Remove HTTP`() {
- assertThat("http://example.com".removeHttp()).isEqualTo("example.com")
- }
-
- @Test
- fun `Remove HTTPS`() {
- assertThat("https://example.com".removeHttp()).isEqualTo("example.com")
- }
-
- @Test
- fun `Remove mixed case`() {
- assertThat("HtTPs://EXAMPLE.Com".removeHttp()).isEqualTo("EXAMPLE.Com")
- }
-
- @Test
- fun `Remove no scheme`() {
- assertThat("example.com".removeHttp()).isEqualTo("example.com")
- }
- }
-
- @Nested
- @DisplayName("URL Validation Tests")
- inner class URLValidationTests {
- @Test
- fun `Validate invalid URL`() {
- assertFalse("this is a test".isValidUrl(), "invalid url")
- }
-
- @Test
- fun `Validate URL`() {
- assertTrue("https://www.example.com".isValidUrl(), "valid url")
- }
- }
-}
diff --git a/src/test/kotlin/net/thauvin/erik/bitly/config/ConfigTest.kt b/src/test/kotlin/net/thauvin/erik/bitly/config/ConfigTest.kt
new file mode 100644
index 0000000..1472d1e
--- /dev/null
+++ b/src/test/kotlin/net/thauvin/erik/bitly/config/ConfigTest.kt
@@ -0,0 +1,105 @@
+/*
+ * ConfigTest.kt
+ *
+ * Copyright 2020-2025 Erik C. Thauvin (erik@thauvin.net)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of this project nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.thauvin.erik.bitly.config
+
+import assertk.assertThat
+import assertk.assertions.isEqualTo
+import net.thauvin.erik.bitly.config.deeplinks.CreateDeeplinks
+import net.thauvin.erik.bitly.config.deeplinks.UpdateDeeplinks
+import net.thauvin.erik.bitly.config.deeplinks.enums.InstallType
+import net.thauvin.erik.bitly.config.deeplinks.enums.Os
+import org.json.JSONObject
+import kotlin.test.Test
+
+class ConfigTest {
+ @Test
+ fun `create config test`() {
+ val deeplinks = CreateDeeplinks().apply {
+ app_id("app_id")
+ install_type(InstallType.AUTO_INSTALL)
+ }
+
+ val config = CreateConfig.Builder("long_url")
+ .domain("domain")
+ .groupGuid("group_guid")
+ .title("title")
+ .tags(arrayOf("tag", "tag2"))
+ .deeplinks(deeplinks)
+ .build()
+
+ val map = mapOf(
+ "long_url" to config.long_url,
+ "domain" to config.domain,
+ "group_guid" to config.group_guid,
+ "title" to config.title,
+ "tags" to config.tags,
+ "deeplinks" to arrayOf(deeplinks.links())
+ )
+
+ assertThat(JSONObject(map).toString()).isEqualTo(
+ """
+ {"group_guid":"group_guid","long_url":"long_url","title":"title","deeplinks":[{"app_id":"app_id","install_type":"auto_install"}],"domain":"domain","tags":["tag","tag2"]}
+ """.trimIndent()
+ )
+ }
+
+ @Test
+ fun `update config test`() {
+ val deeplinks = UpdateDeeplinks().apply {
+ os(Os.IOS)
+ install_type(InstallType.PROMOTE_INSTALL)
+ app_guid("app_guid")
+ }
+
+ val config = UpdateConfig.Builder("blink")
+ .title("title")
+ .archived(true)
+ .tags(arrayOf("tag", "tag2"))
+ .deeplinks(deeplinks)
+ .build()
+
+ val map = mapOf(
+ "bitlink" to config.bitlink,
+ "title" to config.title,
+ "archived" to config.archived,
+ "tags" to config.tags,
+ "deeplinks" to arrayOf(deeplinks.links())
+ )
+
+ assertThat(JSONObject(map).toString()).isEqualTo(
+ """
+ {"archived":true,"bitlink":"blink","title":"title","deeplinks":[{"os":"ios","app_guid":"app_guid","install_type":"promote_install"}],"tags":["tag","tag2"]}
+ """.trimIndent()
+ )
+
+ }
+}
diff --git a/src/test/kotlin/net/thauvin/erik/bitly/config/ConfigTests.kt b/src/test/kotlin/net/thauvin/erik/bitly/config/ConfigTests.kt
deleted file mode 100644
index 42f9b13..0000000
--- a/src/test/kotlin/net/thauvin/erik/bitly/config/ConfigTests.kt
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * ConfigTests.kt
- *
- * Copyright 2020-2025 Erik C. Thauvin (erik@thauvin.net)
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * Neither the name of this project nor the names of its contributors may be
- * used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package net.thauvin.erik.bitly.config
-
-import assertk.all
-import assertk.assertThat
-import assertk.assertions.isEqualTo
-import assertk.assertions.isTrue
-import assertk.assertions.prop
-import net.thauvin.erik.bitly.Constants
-import net.thauvin.erik.bitly.config.deeplinks.CreateDeeplinks
-import net.thauvin.erik.bitly.config.deeplinks.UpdateDeeplinks
-import net.thauvin.erik.bitly.config.deeplinks.enums.InstallType
-import net.thauvin.erik.bitly.config.deeplinks.enums.Os
-import org.json.JSONObject
-import org.junit.jupiter.api.DisplayName
-import org.junit.jupiter.api.Nested
-import kotlin.test.Test
-
-class ConfigTests {
- @Nested
- @DisplayName("Build Configuration Tests")
- inner class BuildConfigurationTests {
- @Test
- fun `Build create configuration`() {
- val deeplinks = CreateDeeplinks().apply {
- app_id("app_id")
- install_type(InstallType.AUTO_INSTALL)
- }
-
- val config = CreateConfig.Builder("long_url")
- .deeplinks(deeplinks)
- .domain("domain")
- .groupGuid("group_guid")
- .tags(listOf("tag", "tag2"))
- .title("title")
- .build()
-
- assertThat(config).all {
- prop(CreateConfig::deeplinks).isEqualTo(deeplinks)
- prop(CreateConfig::domain).isEqualTo("domain")
- prop(CreateConfig::group_guid).isEqualTo("group_guid")
- prop(CreateConfig::long_url).isEqualTo("long_url")
- prop(CreateConfig::tags).isEqualTo(listOf("tag", "tag2"))
- prop(CreateConfig::title).isEqualTo("title")
- prop(CreateConfig::toJson).isEqualTo(false)
- }
-
- val map = mapOf(
- "deeplinks" to listOf(deeplinks.links()),
- "domain" to config.domain,
- "group_guid" to config.group_guid,
- "long_url" to config.long_url,
- "tags" to config.tags,
- "title" to config.title
- )
-
- assertThat(JSONObject(map).toString()).isEqualTo(
- """
- {"group_guid":"group_guid","deeplinks":[{"app_id":"app_id","install_type":"auto_install"}],"long_url":"long_url","title":"title","domain":"domain","tags":["tag","tag2"]}
- """.trimIndent()
- )
- }
- }
-
- @Test
- fun `Build update configuration`() {
- val deeplinks = UpdateDeeplinks().apply {
- os(Os.IOS)
- install_type(InstallType.PROMOTE_INSTALL)
- app_guid("app_guid")
- }
-
- val config = UpdateConfig.Builder("blink")
- .archived(true)
- .deeplinks(deeplinks)
- .tags(listOf("tag", "tag2"))
- .title("title")
- .build()
-
- assertThat(config).all {
- prop(UpdateConfig::archived).isTrue()
- prop(UpdateConfig::bitlink).isEqualTo("blink")
- prop(UpdateConfig::deeplinks).isEqualTo(deeplinks)
- prop(UpdateConfig::tags).isEqualTo(listOf("tag", "tag2"))
- prop(UpdateConfig::title).isEqualTo("title")
- prop(UpdateConfig::toJson).isEqualTo(false)
- }
-
- val map = mapOf(
- "archived" to config.archived,
- "bitlink" to config.bitlink,
- "deeplinks" to listOf(deeplinks.links()),
- "tags" to config.tags,
- "title" to config.title
- )
-
- assertThat(JSONObject(map).toString()).isEqualTo(
- """
- {"archived":true,"bitlink":"blink","deeplinks":[{"os":"ios","app_guid":"app_guid","install_type":"promote_install"}],"title":"title","tags":["tag","tag2"]}
- """.trimIndent()
- )
- }
-
- @Nested
- @DisplayName("Validate Configuration Tests")
- inner class ValidateConfigurationTests {
- @Test
- fun `Validate create configuration`() {
- val deeplinks = CreateDeeplinks().apply {
- app_id("app_id")
- install_type(InstallType.AUTO_INSTALL)
- }
-
- val config = CreateConfig.Builder("long_url")
- .deeplinks(deeplinks)
- .domain("domain")
- .groupGuid("group_guid")
- .tags(listOf("tag", "tag2"))
- .title("title")
- .toJson(true)
-
- assertThat(config).all {
- prop(CreateConfig.Builder::deeplinks).prop(CreateDeeplinks::links).isEqualTo(deeplinks.links())
- prop(CreateConfig.Builder::domain).isEqualTo("domain")
- prop(CreateConfig.Builder::group_guid).isEqualTo("group_guid")
- prop(CreateConfig.Builder::long_url).isEqualTo("long_url")
- prop(CreateConfig.Builder::tags).isEqualTo(listOf("tag", "tag2"))
- prop(CreateConfig.Builder::title).isEqualTo("title")
- prop(CreateConfig.Builder::toJson).isTrue()
- }
-
- config.longUrl("longer_url")
- assertThat(config).prop(CreateConfig.Builder::long_url).isEqualTo("longer_url")
- }
-
- @Test
- fun `Validate create default configuration`() {
- val config = CreateConfig.Builder("long_url")
-
- assertThat(config).all {
- prop(CreateConfig.Builder::long_url).isEqualTo("long_url")
- prop(CreateConfig.Builder::domain).isEqualTo(Constants.EMPTY)
- prop(CreateConfig.Builder::group_guid).isEqualTo(Constants.EMPTY)
- prop(CreateConfig.Builder::title).isEqualTo(Constants.EMPTY)
- prop(CreateConfig.Builder::tags).isEqualTo(emptyList())
- prop(CreateConfig.Builder::deeplinks).prop(CreateDeeplinks::links).isEqualTo(CreateDeeplinks().links())
- prop(CreateConfig.Builder::toJson).isEqualTo(false)
- }
- }
-
- @Test
- fun `Validate update configuration`() {
- val deeplinks = UpdateDeeplinks().apply {
- os(Os.IOS)
- install_type(InstallType.PROMOTE_INSTALL)
- app_guid("app_guid")
- }
-
- val config = UpdateConfig.Builder("bitlink")
- .title("title")
- .archived(true)
- .tags(listOf("tag", "tag2"))
- .deeplinks(deeplinks)
- .toJson(true)
-
- assertThat(config).all {
- prop(UpdateConfig.Builder::bitlink).isEqualTo("bitlink")
- prop(UpdateConfig.Builder::title).isEqualTo("title")
- prop(UpdateConfig.Builder::archived).isTrue()
- prop(UpdateConfig.Builder::tags).isEqualTo(listOf("tag", "tag2"))
- prop(UpdateConfig.Builder::deeplinks).isEqualTo(deeplinks)
- prop(UpdateConfig.Builder::toJson).isTrue()
- }
-
- config.bitlink("blink")
- assertThat(config).prop(UpdateConfig.Builder::bitlink).isEqualTo("blink")
- }
-
- @Test
- fun `Validate update default configuration`() {
- val config = UpdateConfig.Builder("bitlink")
-
- assertThat(config).all {
- prop(UpdateConfig.Builder::bitlink).isEqualTo("bitlink")
- prop(UpdateConfig.Builder::title).isEqualTo(Constants.EMPTY)
- prop(UpdateConfig.Builder::archived).isEqualTo(false)
- prop(UpdateConfig.Builder::tags).isEqualTo(emptyList())
- prop(UpdateConfig.Builder::deeplinks).prop(UpdateDeeplinks::links).isEqualTo(UpdateDeeplinks().links())
- prop(UpdateConfig.Builder::toJson).isEqualTo(false)
- }
- }
- }
-}
diff --git a/src/test/kotlin/net/thauvin/erik/bitly/config/deeplinks/DeeplinksTests.kt b/src/test/kotlin/net/thauvin/erik/bitly/config/deeplinks/DeeplinksTest.kt
similarity index 56%
rename from src/test/kotlin/net/thauvin/erik/bitly/config/deeplinks/DeeplinksTests.kt
rename to src/test/kotlin/net/thauvin/erik/bitly/config/deeplinks/DeeplinksTest.kt
index cf3ecbb..c5dff39 100644
--- a/src/test/kotlin/net/thauvin/erik/bitly/config/deeplinks/DeeplinksTests.kt
+++ b/src/test/kotlin/net/thauvin/erik/bitly/config/deeplinks/DeeplinksTest.kt
@@ -1,5 +1,5 @@
/*
- * DeeplinksTests.kt
+ * DeeplinksTest.kt
*
* Copyright 2020-2025 Erik C. Thauvin (erik@thauvin.net)
*
@@ -31,11 +31,11 @@
package net.thauvin.erik.bitly.config.deeplinks
-import assertk.all
import assertk.assertThat
+import assertk.assertions.contains
+import assertk.assertions.doesNotContain
import assertk.assertions.isEqualTo
import assertk.assertions.isNull
-import assertk.assertions.prop
import net.thauvin.erik.bitly.config.deeplinks.enums.InstallType
import net.thauvin.erik.bitly.config.deeplinks.enums.Os
import org.json.JSONObject
@@ -43,94 +43,90 @@ import org.junit.Test
import java.time.ZoneId
import java.time.ZonedDateTime
-class DeeplinksTests {
+class DeeplinksTest {
@Test
- fun `Create deeplink`() {
+ fun `create test`() {
val deeplinks = CreateDeeplinks().apply {
app_uri_path("app_uri_path")
install_type(InstallType.NO_INSTALL)
}
- assertThat(deeplinks).all {
- prop(CreateDeeplinks::app_id).isNull()
- prop(CreateDeeplinks::app_uri_path).isEqualTo("app_uri_path")
- prop(CreateDeeplinks::install_type).isEqualTo(InstallType.NO_INSTALL)
- prop(CreateDeeplinks::install_url).isNull()
- prop(CreateDeeplinks::links).isEqualTo(deeplinks.links())
- }
-
- assertThat(JSONObject(deeplinks.links()).toString()).isEqualTo(
- """
- {"app_uri_path":"app_uri_path","install_type":"no_install"}
- """.trimIndent()
- )
-
- deeplinks.app_id("app_id")
- deeplinks.install_type(InstallType.PROMOTE_INSTALL)
+ assertThat(deeplinks.install_url()).isNull()
deeplinks.install_url("install_url")
- assertThat(deeplinks).all {
- prop(CreateDeeplinks::app_id).isEqualTo("app_id")
- prop(CreateDeeplinks::install_type).isEqualTo(InstallType.PROMOTE_INSTALL)
- prop(CreateDeeplinks::install_url).isEqualTo("install_url")
- }
+ assertThat(deeplinks.app_uri_path()).isEqualTo("app_uri_path")
+ assertThat(deeplinks.install_type()).isEqualTo(InstallType.NO_INSTALL)
assertThat(JSONObject(deeplinks.links()).toString()).isEqualTo(
"""
- {"install_url":"install_url","app_id":"app_id","app_uri_path":"app_uri_path","install_type":"promote_install"}
+ {"app_uri_path":"app_uri_path","install_type":"no_install","install_url":"install_url"}
""".trimIndent()
)
+
+ deeplinks.install_type(InstallType.PROMOTE_INSTALL)
+ deeplinks.app_id("app_id")
+
+ assertThat(deeplinks.app_id()).isEqualTo("app_id")
+
+ assertThat(JSONObject(deeplinks.links()).toString()).apply {
+ doesNotContain(InstallType.NO_INSTALL.type)
+ contains(InstallType.PROMOTE_INSTALL.type)
+ contains("\"app_id\":\"app_id\"")
+ }
}
@Test
- fun `Update deeplink`() {
+ fun `update test`() {
val deeplinks = UpdateDeeplinks().apply {
app_guid("app_guid")
+ os(Os.IOS)
+ install_type(InstallType.NO_INSTALL)
+ guid("guid")
+ install_url("install_url")
app_uri_path("app_uri_path")
created("created")
- guid("guid")
- install_type(InstallType.NO_INSTALL)
- install_url("install_url")
modified("modified")
- os(Os.IOS)
}
- assertThat(deeplinks).all {
- prop(UpdateDeeplinks::app_guid).isEqualTo("app_guid")
- prop(UpdateDeeplinks::app_uri_path).isEqualTo("app_uri_path")
- prop(UpdateDeeplinks::bitlink).isNull()
- prop(UpdateDeeplinks::brand_guid).isNull()
- prop(UpdateDeeplinks::created).isEqualTo("created")
- prop(UpdateDeeplinks::guid).isEqualTo("guid")
- prop(UpdateDeeplinks::install_type).isEqualTo(InstallType.NO_INSTALL)
- prop(UpdateDeeplinks::install_url).isEqualTo("install_url")
- prop(UpdateDeeplinks::links).isEqualTo(deeplinks.links())
- prop(UpdateDeeplinks::modified).isEqualTo("modified")
- prop(UpdateDeeplinks::os).isEqualTo(Os.IOS)
- }
-
- val zdt = ZonedDateTime.of(1997, 8, 29, 2, 14, 0, 0, ZoneId.of("US/Eastern"))
-
- deeplinks.bitlink("bitlink")
+ assertThat(deeplinks.brand_guid()).isNull()
deeplinks.brand_guid("brand_guid")
- deeplinks.created(zdt)
- deeplinks.install_type(InstallType.PROMOTE_INSTALL)
- deeplinks.modified(zdt)
- deeplinks.os(Os.ANDROID)
- assertThat(deeplinks).all {
- prop(UpdateDeeplinks::bitlink).isEqualTo("bitlink")
- prop(UpdateDeeplinks::brand_guid).isEqualTo("brand_guid")
- prop(UpdateDeeplinks::created).isEqualTo("1997-08-29T02:14:00-0400")
- prop(UpdateDeeplinks::install_type).isEqualTo(InstallType.PROMOTE_INSTALL)
- prop(UpdateDeeplinks::modified).isEqualTo("1997-08-29T02:14:00-0400")
- prop(UpdateDeeplinks::os).isEqualTo(Os.ANDROID)
- }
+ assertThat(deeplinks.app_uri_path()).isEqualTo("app_uri_path")
+ assertThat(deeplinks.install_url()).isEqualTo("install_url")
+
+ assertThat(deeplinks.os()).isEqualTo(Os.IOS)
+ assertThat(deeplinks.install_type()).isEqualTo(InstallType.NO_INSTALL)
+ assertThat(deeplinks.app_guid()).isEqualTo("app_guid")
+ assertThat(deeplinks.modified()).isEqualTo("modified")
+ assertThat(deeplinks.brand_guid()).isEqualTo("brand_guid")
+
assertThat(JSONObject(deeplinks.links()).toString()).isEqualTo(
"""
- {"app_guid":"app_guid","install_url":"install_url","bitlink":"bitlink","os":"android","app_uri_path":"app_uri_path","created":"1997-08-29T02:14:00-0400","brand_guid":"brand_guid","guid":"guid","modified":"1997-08-29T02:14:00-0400","install_type":"promote_install"}
+ {"app_guid":"app_guid","install_url":"install_url","os":"ios","app_uri_path":"app_uri_path","created":"created","brand_guid":"brand_guid","guid":"guid","modified":"modified","install_type":"no_install"}
""".trimIndent()
)
+
+ deeplinks.install_type(InstallType.PROMOTE_INSTALL)
+ deeplinks.os(Os.ANDROID)
+ deeplinks.bitlink("bitlink")
+
+ val zdt = ZonedDateTime.of(1997, 8, 29, 2, 14, 0, 0, ZoneId.of("US/Eastern"))
+ deeplinks.modified(zdt)
+ deeplinks.created(zdt)
+
+ assertThat(deeplinks.bitlink()).isEqualTo("bitlink")
+ assertThat(deeplinks.created()).isEqualTo("1997-08-29T02:14:00-0400")
+ assertThat(deeplinks.modified()).isEqualTo("1997-08-29T02:14:00-0400")
+
+ assertThat(JSONObject(deeplinks.links()).toString()).apply {
+ doesNotContain(InstallType.NO_INSTALL.type)
+ contains(InstallType.PROMOTE_INSTALL.type)
+
+ doesNotContain(Os.IOS.type)
+ contains("\"os\":\"android\"")
+
+ contains("\"bitlink\":\"bitlink\"")
+ }
}
}