From 8f37063beb2d4b92890c4f0f0aa8030605dedd47 Mon Sep 17 00:00:00 2001
From: Weidong Xu <weidxu@microsoft.com>
Date: Fri, 3 Jan 2025 16:04:23 +0800
Subject: [PATCH] http-client-java, generate mock Long number for BigDecimal
 (#5433)

- mock test data in mgmt was not able to handle `BigDecimal` type
https://github.com/Azure/azure-sdk-for-java/pull/43569
- try break the string if too long
https://github.com/Azure/azure-sdk-for-java/pull/43574 (but still have
"code too large" error, as byte code for a method need to be under 64K)
---
 .../core/model/javamodel/JavaPackage.java     |  4 ++++
 .../core/template/ModelTestTemplate.java      | 23 ++++++++++++++++++-
 .../util/ConstantStringTooLongException.java  | 11 +++++++++
 .../core/util/ModelTestCaseUtil.java          |  2 ++
 4 files changed, 39 insertions(+), 1 deletion(-)
 create mode 100644 packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/util/ConstantStringTooLongException.java

diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/javamodel/JavaPackage.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/javamodel/JavaPackage.java
index 349f24462e4..c88a72f7c6e 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/javamodel/JavaPackage.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/model/javamodel/JavaPackage.java
@@ -39,6 +39,7 @@
 import com.microsoft.typespec.http.client.generator.core.template.Templates;
 import com.microsoft.typespec.http.client.generator.core.template.TestProxyAssetsTemplate;
 import com.microsoft.typespec.http.client.generator.core.util.ClientModelUtil;
+import com.microsoft.typespec.http.client.generator.core.util.ConstantStringTooLongException;
 import com.microsoft.typespec.http.client.generator.core.util.PossibleCredentialException;
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -299,6 +300,9 @@ public void addModelUnitTest(ClientModel model) {
         } catch (PossibleCredentialException e) {
             // skip this test file
             logger.warn("Skip unit test for model '{}', caused by key '{}'", model.getName(), e.getKeyName());
+        } catch (ConstantStringTooLongException e) {
+            // skip this test file
+            logger.warn("Skip unit test for model '{}', JSON string is too long.", model.getName());
         }
     }
 
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTestTemplate.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTestTemplate.java
index d7cd6e74f3e..f932d5e8304 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTestTemplate.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/ModelTestTemplate.java
@@ -12,6 +12,7 @@
 import com.microsoft.typespec.http.client.generator.core.model.clientmodel.examplemodel.ExampleNode;
 import com.microsoft.typespec.http.client.generator.core.model.javamodel.JavaFile;
 import com.microsoft.typespec.http.client.generator.core.template.example.ModelExampleWriter;
+import com.microsoft.typespec.http.client.generator.core.util.ConstantStringTooLongException;
 import com.microsoft.typespec.http.client.generator.core.util.ModelExampleUtil;
 import com.microsoft.typespec.http.client.generator.core.util.ModelTestCaseUtil;
 import java.io.ByteArrayOutputStream;
@@ -32,6 +33,18 @@ public static ModelTestTemplate getInstance() {
         return INSTANCE;
     }
 
+    /**
+     * Write the JSON serialization / de-serialization unit test for the model.
+     *
+     * @param model the client model to test.
+     * @param javaFile the java file.
+     * @throws com.microsoft.typespec.http.client.generator.core.util.PossibleCredentialException
+     * thrown when there is possible mock value to a secret property.
+     * Even when the value is mocked, it could be flagged by CI as issue. Therefore, the case is skipped.
+     * @throws com.microsoft.typespec.http.client.generator.core.util.ConstantStringTooLongException
+     * thrown when the String representation of the JSON is too long (>= 2^16).
+     * Constant string of that size would cause compiler "constant string too long" error.
+     */
     @Override
     public void write(ClientModel model, JavaFile javaFile) {
 
@@ -61,12 +74,20 @@ public void write(ClientModel model, JavaFile javaFile) {
 
         javaFile.declareImport(imports);
 
+        String jsonStringExpression = ClassType.STRING.defaultValueExpression(jsonStr);
+        if (jsonStringExpression.length() >= 65536) {
+            // Java compiler would give "constant string too long" error on the generated file.
+            // The length of a string constant in a class file is limited to 2^16 bytes in UTF-8 encoding.
+            // There is also a related "code too large" error, for limit on Java method size in bytecode.
+            throw new ConstantStringTooLongException();
+        }
+
         javaFile.publicFinalClass(model.getName() + "Tests", classBlock -> {
             // testDeserialize
             classBlock.annotation("org.junit.jupiter.api.Test");
             classBlock.publicMethod("void testDeserialize() throws Exception", methodBlock -> {
                 methodBlock.line(String.format("%1$s model = BinaryData.fromString(%2$s).toObject(%1$s.class);",
-                    model.getName(), ClassType.STRING.defaultValueExpression(jsonStr)));
+                    model.getName(), jsonStringExpression));
                 writer.writeAssertion(methodBlock);
             });
 
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/util/ConstantStringTooLongException.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/util/ConstantStringTooLongException.java
new file mode 100644
index 00000000000..998553b83df
--- /dev/null
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/util/ConstantStringTooLongException.java
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.microsoft.typespec.http.client.generator.core.util;
+
+public class ConstantStringTooLongException extends RuntimeException {
+
+    public ConstantStringTooLongException() {
+        super("Constant string too long.");
+    }
+}
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/util/ModelTestCaseUtil.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/util/ModelTestCaseUtil.java
index 02edce96138..5928f6cd53a 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/util/ModelTestCaseUtil.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/util/ModelTestCaseUtil.java
@@ -98,6 +98,8 @@ public static Object jsonFromType(int depth, IType type) {
             return RANDOM.nextInt() & Integer.MAX_VALUE;
         } else if (type.asNullable() == ClassType.LONG) {
             return RANDOM.nextLong() & Long.MAX_VALUE;
+        } else if (type.asNullable() == ClassType.BIG_DECIMAL) {
+            return RANDOM.nextLong() & Long.MAX_VALUE;
         } else if (type.asNullable() == ClassType.FLOAT) {
             return RANDOM.nextFloat() * 100;
         } else if (type.asNullable() == ClassType.DOUBLE) {