Repository: jhipster/jhipster-lite
Pull request #11335 (2024-11-10 15:44:27)

Add Spring Boot LangChain4j sample
Add a sample of using LangChain4J with Spring Boot Web


Patch:
diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml
index a48d69490..8a6d2a3d4 100644
--- a/.github/workflows/github-actions.yml
+++ b/.github/workflows/github-actions.yml
@@ -255,6 +255,7 @@ jobs:
           - customjhlite
           - typescriptapp
           - thymeleafapp
+          - langchain4japp
         include:
           - java-build-tool: maven
           - spring-config-format: yaml
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index abf1d7815..5f9a6563f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -370,6 +370,7 @@ from the main (upstream) repository:
   - reactiveapp
   - customjhlite
   - typescriptapp
+  - langchain4japp
 - Below is the list of build tools that can be used for testing (supported input params for the generate.sh script):
   - gradle
   - maven
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/langchain4j/domain/LangChain4JModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/langchain4j/domain/LangChain4JModuleFactory.java
index 483d6dd54..9465f0473 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/langchain4j/domain/LangChain4JModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/langchain4j/domain/LangChain4JModuleFactory.java
@@ -7,6 +7,7 @@ import tech.jhipster.lite.module.domain.file.JHipsterSource;
 import tech.jhipster.lite.module.domain.javabuild.ArtifactId;
 import tech.jhipster.lite.module.domain.javabuild.GroupId;
 import tech.jhipster.lite.module.domain.javabuild.VersionSlug;
+import tech.jhipster.lite.module.domain.javaproperties.PropertyKey;
 import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
 import tech.jhipster.lite.shared.error.domain.Assert;
 
@@ -22,6 +23,7 @@ public class LangChain4JModuleFactory {
   private static final VersionSlug VERSION_SLUG = versionSlug("langchain4j");
 
   private static final String PROPERTIES = "properties";
+  private static final PropertyKey LANGCHAIN4J_PROPERTY_API_KEY = propertyKey("langchain4j.open-ai.chat-model.api-key");
 
   public JHipsterModule buildModule(JHipsterModuleProperties properties) {
     Assert.notNull(PROPERTIES, properties);
@@ -34,12 +36,15 @@ public class LangChain4JModuleFactory {
         .addDependency(GROUP_ID, OPEN_AI_ARTIFACT_ID, VERSION_SLUG)
         .and()
       .springMainProperties()
-        .set(propertyKey("langchain4j.open-ai.chat-model.api-key"), propertyValue("${OPENAI_API_KEY}"))
-          .comment(propertyKey("langchain4j.open-ai.chat-model.api-key"), comment(API_KEY_DEMO_COMMENT))
+        .set(LANGCHAIN4J_PROPERTY_API_KEY, propertyValue("${OPENAI_API_KEY}"))
+          .comment(LANGCHAIN4J_PROPERTY_API_KEY, comment(API_KEY_DEMO_COMMENT))
         .set(propertyKey("langchain4j.open-ai.chat-model.model-name"), propertyValue("gpt-4o-mini"))
         .set(propertyKey("langchain4j.open-ai.chat-model.log-requests"), propertyValue("true"))
         .set(propertyKey("langchain4j.open-ai.chat-model.log-responses"), propertyValue("true"))
         .and()
+      .springTestProperties()
+        .set(LANGCHAIN4J_PROPERTY_API_KEY, propertyValue("jhipster"))
+        .and()
       .build();
     //@formatter:on
   }
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/sample/langchain4j/application/SampleLangChain4jApplicationService.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/sample/langchain4j/application/SampleLangChain4jApplicationService.java
new file mode 100644
index 000000000..e8fe52f63
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/sample/langchain4j/application/SampleLangChain4jApplicationService.java
@@ -0,0 +1,20 @@
+package tech.jhipster.lite.generator.server.springboot.mvc.sample.langchain4j.application;
+
+import org.springframework.stereotype.Service;
+import tech.jhipster.lite.generator.server.springboot.mvc.sample.langchain4j.domain.SampleLangChain4jModuleFactory;
+import tech.jhipster.lite.module.domain.JHipsterModule;
+import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
+
+@Service
+public class SampleLangChain4jApplicationService {
+
+  private final SampleLangChain4jModuleFactory factory;
+
+  public SampleLangChain4jApplicationService() {
+    factory = new SampleLangChain4jModuleFactory();
+  }
+
+  public JHipsterModule buildModule(JHipsterModuleProperties properties) {
+    return factory.buildModule(properties);
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/sample/langchain4j/domain/SampleLangChain4jModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/sample/langchain4j/domain/SampleLangChain4jModuleFactory.java
new file mode 100644
index 000000000..8edc458db
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/sample/langchain4j/domain/SampleLangChain4jModuleFactory.java
@@ -0,0 +1,39 @@
+package tech.jhipster.lite.generator.server.springboot.mvc.sample.langchain4j.domain;
+
+import static tech.jhipster.lite.module.domain.JHipsterModule.*;
+
+import tech.jhipster.lite.module.domain.JHipsterModule;
+import tech.jhipster.lite.module.domain.file.JHipsterDestination;
+import tech.jhipster.lite.module.domain.file.JHipsterSource;
+import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
+import tech.jhipster.lite.shared.error.domain.Assert;
+
+public class SampleLangChain4jModuleFactory {
+
+  private static final String SAMPLE = "sample";
+
+  private static final JHipsterSource SOURCE = from("server/springboot/mvc/sample/langchain4j");
+
+  private static final String PRIMARY = "infrastructure/primary";
+
+  public JHipsterModule buildModule(JHipsterModuleProperties properties) {
+    Assert.notNull("properties", properties);
+
+    String packagePath = properties.packagePath();
+    JHipsterDestination mainDestination = toSrcMainJava().append(packagePath).append(SAMPLE);
+    JHipsterDestination testDestination = toSrcTestJava().append(packagePath).append(SAMPLE);
+
+    //@formatter:off
+    return moduleBuilder(properties)
+      .files()
+        .batch(SOURCE.append("main").append(PRIMARY), mainDestination.append(PRIMARY))
+          .addTemplate("ChatResource.java")
+          .and()
+        .batch(SOURCE.append("test").append(SAMPLE).append(PRIMARY), testDestination.append(PRIMARY))
+          .addTemplate("ChatResourceTest.java")
+          .and()
+        .and()
+      .build();
+    //@formatter:on
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/sample/langchain4j/infrastructure/primary/SampleLangChain4jModuleConfiguration.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/sample/langchain4j/infrastructure/primary/SampleLangChain4jModuleConfiguration.java
new file mode 100644
index 000000000..92e0f6322
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/sample/langchain4j/infrastructure/primary/SampleLangChain4jModuleConfiguration.java
@@ -0,0 +1,28 @@
+package tech.jhipster.lite.generator.server.springboot.mvc.sample.langchain4j.infrastructure.primary;
+
+import static tech.jhipster.lite.shared.slug.domain.JHLiteFeatureSlug.SPRING_MVC_SERVER;
+import static tech.jhipster.lite.shared.slug.domain.JHLiteModuleSlug.*;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import tech.jhipster.lite.generator.server.springboot.mvc.sample.langchain4j.application.SampleLangChain4jApplicationService;
+import tech.jhipster.lite.module.domain.resource.JHipsterModuleOrganization;
+import tech.jhipster.lite.module.domain.resource.JHipsterModulePropertiesDefinition;
+import tech.jhipster.lite.module.domain.resource.JHipsterModuleResource;
+
+@Configuration
+class SampleLangChain4jModuleConfiguration {
+
+  @Bean
+  JHipsterModuleResource langChain4jResourceInit(SampleLangChain4jApplicationService applicationService) {
+    return JHipsterModuleResource.builder()
+      .slug(SPRING_BOOT_LANGCHAIN4J_SAMPLE)
+      .propertiesDefinition(
+        JHipsterModulePropertiesDefinition.builder().addBasePackage().addIndentation().addSpringConfigurationFormat().build()
+      )
+      .apiDoc("Spring Boot - LangChain4j", "Add LangChain4j sample")
+      .organization(JHipsterModuleOrganization.builder().addDependency(SPRING_MVC_SERVER).addDependency(LANGCHAIN4J).build())
+      .tags("spring-boot", "spring", "server", "langchain4j")
+      .factory(applicationService::buildModule);
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/sample/langchain4j/package-info.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/sample/langchain4j/package-info.java
new file mode 100644
index 000000000..a0e75e2f4
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/sample/langchain4j/package-info.java
@@ -0,0 +1,2 @@
+@tech.jhipster.lite.BusinessContext
+package tech.jhipster.lite.generator.server.springboot.mvc.sample.langchain4j;
diff --git a/src/main/java/tech/jhipster/lite/shared/error/domain/Assert.java b/src/main/java/tech/jhipster/lite/shared/error/domain/Assert.java
index 77213a8b5..08a0a2f60 100644
--- a/src/main/java/tech/jhipster/lite/shared/error/domain/Assert.java
+++ b/src/main/java/tech/jhipster/lite/shared/error/domain/Assert.java
@@ -368,11 +368,11 @@ public final class Assert {
     }
 
     /**
-     * Ensure that the value does not contain whitespace
+     * Ensure  that the value does not contain whitespace
      *
      * @return The current asserter
      * @throws MissingMandatoryValueException
-     *           if the value contains whitespace
+     *           if the value contain whitespace
      */
     public StringAsserter noWhitespace() {
       notNull();
diff --git a/src/main/java/tech/jhipster/lite/shared/slug/domain/JHLiteModuleSlug.java b/src/main/java/tech/jhipster/lite/shared/slug/domain/JHLiteModuleSlug.java
index 7a10ba755..66dd0ef69 100644
--- a/src/main/java/tech/jhipster/lite/shared/slug/domain/JHLiteModuleSlug.java
+++ b/src/main/java/tech/jhipster/lite/shared/slug/domain/JHLiteModuleSlug.java
@@ -157,7 +157,8 @@ public enum JHLiteModuleSlug implements JHipsterModuleSlugFactory {
   VUE_PINIA("vue-pinia"),
   TS_PAGINATION_DOMAIN("ts-pagination-domain"),
   TS_REST_PAGINATION("ts-rest-pagination"),
-  LANGCHAIN4J("langchain4j");
+  LANGCHAIN4J("langchain4j"),
+  SPRING_BOOT_LANGCHAIN4J_SAMPLE("spring-boot-langchain4j-sample");
 
   private static final Map<String, JHLiteModuleSlug> moduleSlugMap = Stream.of(values()).collect(
     Collectors.toMap(JHLiteModuleSlug::get, Function.identity())
diff --git a/src/main/resources/generator/dependencies/angular/package.json b/src/main/resources/generator/dependencies/angular/package.json
index bc9071eea..09d8b179e 100644
--- a/src/main/resources/generator/dependencies/angular/package.json
+++ b/src/main/resources/generator/dependencies/angular/package.json
@@ -2,11 +2,11 @@
   "dependencies": {
     "@angular-builders/jest": "18.0.0",
     "@angular/build": "18.2.11",
-    "@angular/cdk": "18.2.12",
+    "@angular/cdk": "18.2.11",
     "@angular/cli": "18.2.11",
-    "@angular/compiler-cli": "18.2.11",
-    "@angular/core": "18.2.11",
-    "@angular/material": "18.2.12",
+    "@angular/compiler-cli": "18.2.10",
+    "@angular/core": "18.2.10",
+    "@angular/material": "18.2.11",
     "angular-eslint": "18.4.0",
     "jest-environment-jsdom": "29.7.0",
     "jest-preset-angular": "14.2.4",
diff --git a/src/main/resources/generator/dependencies/react/package.json b/src/main/resources/generator/dependencies/react/package.json
index bf78fd076..f6b7d49f2 100644
--- a/src/main/resources/generator/dependencies/react/package.json
+++ b/src/main/resources/generator/dependencies/react/package.json
@@ -18,6 +18,7 @@
     "react-dom": "18.3.1",
     "react-hook-form": "7.53.1",
     "react-i18next": "15.1.0",
+    "react-scripts": "5.0.1",
     "sass": "1.80.6",
     "ts-node": "10.9.2"
   }
diff --git a/src/main/resources/generator/server/springboot/mvc/internationalized-errors/resources/assertions-errors/assertion-errors-messages.properties b/src/main/resources/generator/server/springboot/mvc/internationalized-errors/resources/assertions-errors/assertion-errors-messages.properties
index 713dbea3a..7283baa26 100644
--- a/src/main/resources/generator/server/springboot/mvc/internationalized-errors/resources/assertions-errors/assertion-errors-messages.properties
+++ b/src/main/resources/generator/server/springboot/mvc/internationalized-errors/resources/assertions-errors/assertion-errors-messages.properties
@@ -29,5 +29,5 @@ assertion-error.STRING_TOO_SHORT.title=String too short
 assertion-error.STRING_WITH_WHITESPACES.detail=The string {{ field }} must not have any whitespaces
 assertion-error.STRING_WITH_WHITESPACES.title=String with whitespaces
 
-assertion-error.TOO_MANY_ELEMENTS.detail=There are too many elements in {{ field }}, max is {{ maxSize }} (current {{ currentSize }} element(s))
+assertion-error.TOO_MANY_ELEMENTS.detail=There is too many elements in {{ field }}, max is {{ maxSize }} (current {{ currentSize }} element(s))
 assertion-error.TOO_MANY_ELEMENTS.title=Too many elements
diff --git a/src/main/resources/generator/server/springboot/mvc/internationalized-errors/resources/errors/application-errors-messages_fr.properties b/src/main/resources/generator/server/springboot/mvc/internationalized-errors/resources/errors/application-errors-messages_fr.properties
index 07046ab50..08ab58f76 100644
--- a/src/main/resources/generator/server/springboot/mvc/internationalized-errors/resources/errors/application-errors-messages_fr.properties
+++ b/src/main/resources/generator/server/springboot/mvc/internationalized-errors/resources/errors/application-errors-messages_fr.properties
@@ -2,7 +2,7 @@
 ################################# Messages for errors #################################
 #######################################################################################
 
-error.bad-request.detail=Votre requête n'est pas valide : {{ code }}
+error.bad-request.detail=Votre requête n'est pas valide: {{ code }}
 error.bad-request.title=Bad request
 
 error.internal-server-error.detail=Une erreur est survenue
diff --git a/src/main/resources/generator/server/springboot/mvc/sample/langchain4j/main/infrastructure/primary/ChatResource.java.mustache b/src/main/resources/generator/server/springboot/mvc/sample/langchain4j/main/infrastructure/primary/ChatResource.java.mustache
new file mode 100644
index 000000000..5098457c4
--- /dev/null
+++ b/src/main/resources/generator/server/springboot/mvc/sample/langchain4j/main/infrastructure/primary/ChatResource.java.mustache
@@ -0,0 +1,23 @@
+package {{packageName}}.sample.infrastructure.primary;
+
+import dev.langchain4j.model.chat.ChatLanguageModel;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/api")
+class ChatResource {
+
+  private final ChatLanguageModel chatLanguageModel;
+
+  public ChatResource(ChatLanguageModel chatLanguageModel) {
+    this.chatLanguageModel = chatLanguageModel;
+  }
+
+  @GetMapping("/chat")
+  public String send(@RequestParam(value = "message", defaultValue = "Hello") String message) {
+    return chatLanguageModel.generate(message);
+  }
+}
diff --git a/src/main/resources/generator/server/springboot/mvc/security/jwt/basic-auth/main/infrastructure/primary/RestAuthenticationQuery.java.mustache b/src/main/resources/generator/server/springboot/mvc/security/jwt/basic-auth/main/infrastructure/primary/RestAuthenticationQuery.java.mustache
index 0b86778f0..acc87a63b 100644
--- a/src/main/resources/generator/server/springboot/mvc/security/jwt/basic-auth/main/infrastructure/primary/RestAuthenticationQuery.java.mustache
+++ b/src/main/resources/generator/server/springboot/mvc/security/jwt/basic-auth/main/infrastructure/primary/RestAuthenticationQuery.java.mustache
@@ -13,7 +13,7 @@ import {{packageName}}.account.infrastructure.primary.RestAuthenticationQuery.Re
 import {{packageName}}.shared.authentication.domain.Roles;
 
 @JsonDeserialize(builder = RestAuthenticationQueryBuilder.class)
-@Schema(name = "AuthenticationQuery", description = "Query to authenticate a user")
+@Schema(name = "AuthenticationQuery", description = "Query to authenticate au user")
 final class RestAuthenticationQuery {
 
   private final String username;
diff --git a/src/main/resources/generator/server/springboot/mvc/security/oauth2/core/docker/jhipster-realm.json.mustache b/src/main/resources/generator/server/springboot/mvc/security/oauth2/core/docker/jhipster-realm.json.mustache
index 226b81e74..a74ae77ff 100644
--- a/src/main/resources/generator/server/springboot/mvc/security/oauth2/core/docker/jhipster-realm.json.mustache
+++ b/src/main/resources/generator/server/springboot/mvc/security/oauth2/core/docker/jhipster-realm.json.mustache
@@ -1807,7 +1807,7 @@
     {
       "id": "da7d3a39-7077-4354-9ffc-5b9f79fbaf0d",
       "alias": "Verify Existing Account by Re-authentication",
-      "description": "Re-authentication of existing account",
+      "description": "Reauthentication of existing account",
       "providerId": "basic-flow",
       "topLevel": false,
       "builtIn": true,
diff --git a/src/main/resources/messages/assertions-errors/assertion-errors-messages.properties b/src/main/resources/messages/assertions-errors/assertion-errors-messages.properties
index bdf719eb6..af994cff4 100644
--- a/src/main/resources/messages/assertions-errors/assertion-errors-messages.properties
+++ b/src/main/resources/messages/assertions-errors/assertion-errors-messages.properties
@@ -28,7 +28,7 @@ assertion-error.STRING_TOO_SHORT.title=String too short
 assertion-error.STRING_WITH_WHITESPACES.detail=The string {{ field }} must not have any whitespaces
 assertion-error.STRING_WITH_WHITESPACES.title=String with whitespaces
 
-assertion-error.TOO_MANY_ELEMENTS.detail=There are too many elements in {{ field }}, max is {{ maxSize }} (current {{ currentSize }} element(s))
+assertion-error.TOO_MANY_ELEMENTS.detail=There is too many elements in {{ field }}, max is {{ maxSize }} (current {{ currentSize }} element(s))
 assertion-error.TOO_MANY_ELEMENTS.title=Too many elements
 
 assertion-error.STRING_NOT_MATCHING_PATTERN.detail=The string {{ field }} must match the pattern {{ pattern }}


Test patch:
diff --git a/src/main/resources/generator/server/springboot/mvc/internationalized-errors/test/ApplicationErrorsHandlerIT.java.mustache b/src/main/resources/generator/server/springboot/mvc/internationalized-errors/test/ApplicationErrorsHandlerIT.java.mustache
index be0c341cf..dc839c15a 100644
--- a/src/main/resources/generator/server/springboot/mvc/internationalized-errors/test/ApplicationErrorsHandlerIT.java.mustache
+++ b/src/main/resources/generator/server/springboot/mvc/internationalized-errors/test/ApplicationErrorsHandlerIT.java.mustache
@@ -32,6 +32,6 @@ class {{ baseName }}ErrorsHandlerIT {
       .perform(get("/api/errors/bad-request").locale(Locale.FRANCE))
       .andExpect(status().is4xxClientError())
       .andExpect(jsonPath("title").value("Bad request"))
-      .andExpect(jsonPath("detail").value("Votre requête n'est pas valide : 400"));
+      .andExpect(jsonPath("detail").value("Votre requête n'est pas valide: 400"));
   }
 }
diff --git a/src/main/resources/generator/server/springboot/mvc/internationalized-errors/test/AssertionErrorsHandlerIT.java.mustache b/src/main/resources/generator/server/springboot/mvc/internationalized-errors/test/AssertionErrorsHandlerIT.java.mustache
index 56deb3d7d..3ccdb6c20 100644
--- a/src/main/resources/generator/server/springboot/mvc/internationalized-errors/test/AssertionErrorsHandlerIT.java.mustache
+++ b/src/main/resources/generator/server/springboot/mvc/internationalized-errors/test/AssertionErrorsHandlerIT.java.mustache
@@ -93,7 +93,7 @@ class AssertionErrorsHandlerIT {
       .perform(get("/api/assertion-errors/too-many-elements"))
       .andExpect(status().isBadRequest())
       .andExpect(jsonPath("title").value("Too many elements"))
-      .andExpect(jsonPath("detail").value("There are too many elements in myField, max is 1 (current 2 element(s))"))
+      .andExpect(jsonPath("detail").value("There is too many elements in myField, max is 1 (current 2 element(s))"))
       .andExpect(jsonPath("key").value("TOO_MANY_ELEMENTS"));
   }
 
diff --git a/src/main/resources/generator/server/springboot/mvc/sample/langchain4j/test/sample/infrastructure/primary/ChatResourceTest.java.mustache b/src/main/resources/generator/server/springboot/mvc/sample/langchain4j/test/sample/infrastructure/primary/ChatResourceTest.java.mustache
new file mode 100644
index 000000000..8ebb8a53d
--- /dev/null
+++ b/src/main/resources/generator/server/springboot/mvc/sample/langchain4j/test/sample/infrastructure/primary/ChatResourceTest.java.mustache
@@ -0,0 +1,32 @@
+package {{packageName}}.sample.infrastructure.primary;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import {{packageName}}.UnitTest;
+import dev.langchain4j.model.chat.ChatLanguageModel;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@UnitTest
+@ExtendWith(MockitoExtension.class)
+class ChatResourceTest {
+
+  private static final String ANSWER = "Hello! How can I assist you today?";
+
+  @Mock
+  private ChatLanguageModel chatLanguageModel;
+
+  @InjectMocks
+  private ChatResource chat;
+
+  @Test
+  void shouldSendMessage() {
+   when(chatLanguageModel.generate("Hello")).thenReturn(ANSWER);
+
+    assertThat(chat.send("Hello")).isEqualTo(ANSWER);
+  }
+}
diff --git a/src/test/features/server/springboot/langchain4j.feature b/src/test/features/server/springboot/langchain4j.feature
index 38975e081..7347f894f 100644
--- a/src/test/features/server/springboot/langchain4j.feature
+++ b/src/test/features/server/springboot/langchain4j.feature
@@ -8,3 +8,10 @@ Feature: LangChain4j module
     Then I should have entries in "src/main/resources/config/application.yml"
       | open-ai     |
       | langchain4j |
+
+  Scenario: Should add Spring Boot LangChain4j Sample
+    When I apply "spring-boot-langchain4j-sample" module to default project with maven file
+      | packageName | tech.jhipster.chips |
+      | baseName    | jhipster            |
+    Then I should have files in "src/main/java/tech/jhipster/chips/sample/infrastructure/primary"
+      | ChatResource.java |
diff --git a/src/test/java/tech/jhipster/lite/generator/server/springboot/langchain4j/domain/LangChain4JModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/server/springboot/langchain4j/domain/LangChain4JModuleFactoryTest.java
index fb51b5116..15e4afb3c 100644
--- a/src/test/java/tech/jhipster/lite/generator/server/springboot/langchain4j/domain/LangChain4JModuleFactoryTest.java
+++ b/src/test/java/tech/jhipster/lite/generator/server/springboot/langchain4j/domain/LangChain4JModuleFactoryTest.java
@@ -62,6 +62,16 @@ class LangChain4JModuleFactoryTest {
               log-responses: 'true'
               model-name: gpt-4o-mini
         """
+      )
+      .and()
+      .hasFile("src/test/resources/config/application-test.yml")
+      .containing(
+        """
+        langchain4j:
+          open-ai:
+            chat-model:
+              api-key: jhipster
+        """
       );
   }
 }
diff --git a/src/test/java/tech/jhipster/lite/shared/error/infrastructure/primary/AssertionErrorsHandlerIT.java b/src/test/java/tech/jhipster/lite/shared/error/infrastructure/primary/AssertionErrorsHandlerIT.java
index bd7810b4e..a19b01da8 100644
--- a/src/test/java/tech/jhipster/lite/shared/error/infrastructure/primary/AssertionErrorsHandlerIT.java
+++ b/src/test/java/tech/jhipster/lite/shared/error/infrastructure/primary/AssertionErrorsHandlerIT.java
@@ -94,7 +94,7 @@ class AssertionErrorsHandlerIT {
       .perform(get("/api/assertion-errors/too-many-elements"))
       .andExpect(status().isBadRequest())
       .andExpect(jsonPath("title").value("Too many elements"))
-      .andExpect(jsonPath("detail").value("There are too many elements in myField, max is 1 (current 2 element(s))"))
+      .andExpect(jsonPath("detail").value("There is too many elements in myField, max is 1 (current 2 element(s))"))
       .andExpect(jsonPath("key").value("TOO_MANY_ELEMENTS"));
   }
 
diff --git a/src/test/webapp/unit/common/primary/toast/Toast.spec.ts b/src/test/webapp/unit/common/primary/toast/Toast.spec.ts
index 4c24ba18a..d1efd728b 100644
--- a/src/test/webapp/unit/common/primary/toast/Toast.spec.ts
+++ b/src/test/webapp/unit/common/primary/toast/Toast.spec.ts
@@ -7,7 +7,7 @@ import { ToastVue } from '@/shared/toast/infrastructure/primary';
 import { ToastType } from '@/shared/toast/infrastructure/primary/ToastType';
 import { shallowMount, VueWrapper } from '@vue/test-utils';
 import { describe, expect, it, vi } from 'vitest';
-import { AlertListenerFixture, stubAlertListener } from '../../../shared/alert/domain/AlertListener.fixture';
+import { AlertListenerFixture, stubAlertListener } from '../../../shared/alert/domain/AlertListener.fixure';
 import { stubTimeout } from '../timeout/Timeout.fixture';
 
 let wrapper: VueWrapper;
diff --git a/src/test/webapp/unit/router/index.spec.ts b/src/test/webapp/unit/router/index.spec.ts
index 4fef3fb2e..7c3e43570 100644
--- a/src/test/webapp/unit/router/index.spec.ts
+++ b/src/test/webapp/unit/router/index.spec.ts
@@ -39,7 +39,7 @@ import { ProjectFoldersRepositoryStub, stubProjectFoldersRepository } from '../m
 import { LocalWindowThemeRepositoryStub, stubLocalWindowThemeRepository } from '../module/domain/ThemeRepository.fixture';
 import { stubWindow } from '../module/primary/GlobalWindow.fixture';
 import { stubAlertBus } from '../shared/alert/domain/AlertBus.fixture';
-import { stubAlertListener } from '../shared/alert/domain/AlertListener.fixture';
+import { stubAlertListener } from '../shared/alert/domain/AlertListener.fixure';
 
 interface ApplicationListenerStub extends ApplicationListener {
   addEventListener: vi.fn;
diff --git a/src/test/webapp/unit/shared/alert/domain/AlertListener.fixture.ts b/src/test/webapp/unit/shared/alert/domain/AlertListener.fixure.ts
similarity index 100%
rename from src/test/webapp/unit/shared/alert/domain/AlertListener.fixture.ts
rename to src/test/webapp/unit/shared/alert/domain/AlertListener.fixure.ts
diff --git a/tests-ci/generate.sh b/tests-ci/generate.sh
index bf3e521f7..fecefbb24 100755
--- a/tests-ci/generate.sh
+++ b/tests-ci/generate.sh
@@ -508,8 +508,17 @@ elif [[ $application == 'thymeleafapp' ]]; then
     "htmx-webjars" \
     "thymeleaf-template-htmx-webjars"
 
+elif [[ $application == 'langchain4japp' ]]; then
+  init_server
+  spring_boot_mvc
+  sonar_back
+
+  applyModules \
+    "langchain4j" \
+    "spring-boot-langchain4j-sample"
+
 else
-  echo "*** Unknown configuration..."
+  echo "*** Unknown configuration..." "$application"
   exit 1
 fi