From a1a07bd1ad801ba172200e5cc85ea4850fd8603e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:59:43 +0000 Subject: [PATCH 01/25] fix(deps): replace xtext.version with org.eclipse.xtext:xtend-maven-plugin --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3c0f1136..31e0f7e4 100644 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ 9.75.0 - 2.38.0 + 2.41.0 6.0.0 2.0.17 From ebd7fd12903faf9dc84feaeee4f1ecca9f98b1a8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 7 Jan 2026 16:00:03 +0000 Subject: [PATCH 02/25] chore(deps): update dependency org.apache.maven.plugins:maven-resources-plugin to v3.4.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3c0f1136..723daf9b 100644 --- a/pom.xml +++ b/pom.xml @@ -105,7 +105,7 @@ 3.1.1 1.6.13 3.2.8 - 3.3.1 + 3.4.0 build/common-domain-model/rosetta-source/src/main/rosetta target/python-cdm From 394400db920a4ba52571ba7b6eb7c03cb3ab9795 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 7 Jan 2026 16:00:18 +0000 Subject: [PATCH 03/25] fix(deps): update dependency org.apache.commons:commons-lang3 to v3.20.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3c0f1136..82c2e6b2 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ [21,22) 21 - 3.19.0 + 3.20.0 1.11.0 2.21.0 33.3.1-jre From afe186856ea400214fc110f254b7ef05d6b9db8c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 7 Jan 2026 16:00:22 +0000 Subject: [PATCH 04/25] fix(deps): update dependency org.apache.commons:commons-text to v1.15.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3c0f1136..83d92b7b 100644 --- a/pom.xml +++ b/pom.xml @@ -72,7 +72,7 @@ 21 3.19.0 - 1.11.0 + 1.15.0 2.21.0 33.3.1-jre From 3d20728a37dac27a37b6bd4e45c8e3d0df60a1ba Mon Sep 17 00:00:00 2001 From: dschwartznyc Date: Thu, 22 Jan 2026 18:20:14 -0500 Subject: [PATCH 05/25] chore: remove renovate.yml --- .github/workflows/renovate.yml | 38 ---------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 .github/workflows/renovate.yml diff --git a/.github/workflows/renovate.yml b/.github/workflows/renovate.yml deleted file mode 100644 index 205db572..00000000 --- a/.github/workflows/renovate.yml +++ /dev/null @@ -1,38 +0,0 @@ -# .github/workflows/renovate.yml -# This workflow runs Renovate Bot on a schedule or on demand. -# It uses the configuration file located at .github/renovate.json - -name: Renovate - -on: - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - # Runs the workflow on a schedule (e.g., every day at 2 AM) - schedule: - - cron: '0 2 * * *' - push: - paths: - - .github/renovate.json - - .github/workflows/renovate.yml - -jobs: - renovate: - runs-on: ubuntu-latest - # 👇 Expanded permissions so Renovate can do everything it needs - permissions: - contents: write # push branches, update files - pull-requests: write # open/update PRs - issues: write # create/update Dependency Dashboard issue - security-events: read # read Dependabot vulnerability alerts - steps: - # Checks out the repository under $GITHUB_WORKSPACE - - uses: actions/checkout@v6 - - # Runs the Renovate GitHub Action - - name: Renovate - uses: renovatebot/github-action@v44.2.5 - with: - token: ${{ secrets.GITHUB_TOKEN }} # required to create PRs/issues - configurationFile: .github/renovate.json - env: - RENOVATE_REPOSITORIES: ${{ github.repository }} # scan current repo \ No newline at end of file From c16fa9c6dd844a529e23864a10a158555074bb16 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 26 Jan 2026 02:57:34 +0000 Subject: [PATCH 06/25] fix(deps): update logback monorepo to v1.5.26 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 45bf49c8..3efcdcb1 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ 6.0.0 2.0.17 - 1.5.21 + 1.5.26 6.0.1 From 341c03424ed39064f0fbe4426bddf4e120c9fb6a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 26 Jan 2026 16:21:37 +0000 Subject: [PATCH 07/25] fix(deps): update dependency org.junit:junit-bom to v6.0.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 45bf49c8..de9bc6af 100644 --- a/pom.xml +++ b/pom.xml @@ -90,7 +90,7 @@ 1.5.21 - 6.0.1 + 6.0.2 3.0 From cb65b65923ac89d48cc6c59b8668c17f372e9b52 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 14:56:54 +0000 Subject: [PATCH 08/25] fix(deps): update rosetta.dsl.version to v9.76.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index de9bc6af..3d732cb3 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ s01.oss.sonatype.org 20 - 9.75.3 + 9.76.1 2.38.0 6.0.0 From dc9e25207e686ed88782aecf9c57689ed7af1ac6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 21:44:32 +0000 Subject: [PATCH 09/25] chore(deps): update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.15.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3d732cb3..a050f154 100644 --- a/pom.xml +++ b/pom.xml @@ -97,7 +97,7 @@ 3.6.2 3.6.0 3.5.0 - 3.14.1 + 3.15.0 3.5.4 3.1.1 3.1.4 From 4248283244a00722e96ee2b940ac011dcca216a3 Mon Sep 17 00:00:00 2001 From: dschwartznyc Date: Sun, 8 Feb 2026 15:17:47 -0500 Subject: [PATCH 10/25] feat: Refactor CLI to return exit codes and add options for validation error/warning handling, accompanied by new unit tests. --- .../python/PythonCodeGeneratorCLI.java | 73 ++++++--- .../python/PythonCodeGeneratorCLITest.java | 146 ++++++++++++++++++ 2 files changed, 195 insertions(+), 24 deletions(-) create mode 100644 src/test/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLITest.java diff --git a/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLI.java b/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLI.java index 71604890..d98f4b21 100644 --- a/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLI.java +++ b/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLI.java @@ -79,6 +79,10 @@ public class PythonCodeGeneratorCLI { private static final Logger LOGGER = LoggerFactory.getLogger(PythonCodeGeneratorCLI.class); public static void main(String[] args) { + System.exit(new PythonCodeGeneratorCLI().execute(args)); + } + + public int execute(String[] args) { System.out.println("***** Running PythonCodeGeneratorCLI v2 *****"); Options options = new Options(); Option help = new Option("h", "Print usage"); @@ -88,33 +92,44 @@ public static void main(String[] args) { .build(); Option tgtDirOpt = Option.builder("t").longOpt("tgt").argName("tgtDir") .desc("Target Python directory (default: ./python)").hasArg().build(); + Option allowErrorsOpt = Option.builder("e").longOpt("allow-errors") + .desc("Continue generation even if validation errors occur").build(); + Option failOnWarningsOpt = Option.builder("w").longOpt("fail-on-warnings") + .desc("Treat validation warnings as errors").build(); options.addOption(help); options.addOption(srcDirOpt); options.addOption(srcFileOpt); options.addOption(tgtDirOpt); + options.addOption(allowErrorsOpt); + options.addOption(failOnWarningsOpt); CommandLineParser parser = new DefaultParser(); try { CommandLine cmd = parser.parse(options, args); if (cmd.hasOption("h")) { printUsage(options); - return; + return 0; } String tgtDir = cmd.getOptionValue("t", "./python"); + boolean allowErrors = cmd.hasOption("e"); + boolean failOnWarnings = cmd.hasOption("w"); + if (cmd.hasOption("s")) { String srcDir = cmd.getOptionValue("s"); - translateFromSourceDir(srcDir, tgtDir); + return translateFromSourceDir(srcDir, tgtDir, allowErrors, failOnWarnings); } else if (cmd.hasOption("f")) { String srcFile = cmd.getOptionValue("f"); - translateFromSourceFile(srcFile, tgtDir); + return translateFromSourceFile(srcFile, tgtDir, allowErrors, failOnWarnings); } else { System.err.println("Either a source directory (-s) or source file (-f) must be specified."); printUsage(options); + return 1; } } catch (ParseException e) { System.err.println("Failed to parse command line arguments: " + e.getMessage()); printUsage(options); + return 1; } } @@ -123,53 +138,59 @@ private static void printUsage(Options options) { formatter.printHelp("PythonCodeGeneratorCLI", options, true); } - private static void translateFromSourceDir(String srcDir, String tgtDir) { + protected int translateFromSourceDir(String srcDir, String tgtDir, boolean allowErrors, boolean failOnWarnings) { // Find all .rosetta files in a directory Path srcDirPath = Paths.get(srcDir); if (!Files.exists(srcDirPath)) { LOGGER.error("Source directory does not exist: {}", srcDir); - System.exit(1); + return 1; } if (!Files.isDirectory(srcDirPath)) { LOGGER.error("Source directory is not a directory: {}", srcDir); - System.exit(1); + return 1; } try { List rosettaFiles = Files.walk(srcDirPath) .filter(Files::isRegularFile) .filter(f -> f.getFileName().toString().endsWith(".rosetta")) .collect(Collectors.toList()); - processRosettaFiles(rosettaFiles, tgtDir); + return processRosettaFiles(rosettaFiles, tgtDir, allowErrors, failOnWarnings); } catch (IOException e) { LOGGER.error("Failed to process source directory: {}", srcDir, e); + return 1; } } - private static void translateFromSourceFile(String srcFile, String tgtDir) { + protected int translateFromSourceFile(String srcFile, String tgtDir, boolean allowErrors, boolean failOnWarnings) { Path srcFilePath = Paths.get(srcFile); if (!Files.exists(srcFilePath)) { LOGGER.error("Source file does not exist: {}", srcFile); - System.exit(1); + return 1; } if (Files.isDirectory(srcFilePath)) { LOGGER.error("Source file is a directory: {}", srcFile); - System.exit(1); + return 1; } if (!srcFilePath.toString().endsWith(".rosetta")) { LOGGER.error("Source file does not end with .rosetta: {}", srcFile); - System.exit(1); + return 1; } List rosettaFiles = List.of(srcFilePath); - processRosettaFiles(rosettaFiles, tgtDir); + return processRosettaFiles(rosettaFiles, tgtDir, allowErrors, failOnWarnings); + } + + protected IResourceValidator getValidator(Injector injector) { + return injector.getInstance(IResourceValidator.class); } // Common processing function - private static void processRosettaFiles(List rosettaFiles, String tgtDir) { + protected int processRosettaFiles(List rosettaFiles, String tgtDir, boolean allowErrors, + boolean failOnWarnings) { LOGGER.info("Processing {} .rosetta files, writing to: {}", rosettaFiles.size(), tgtDir); if (rosettaFiles.isEmpty()) { System.err.println("No .rosetta files found to process."); - System.exit(1); + return 1; } Injector injector = new PythonRosettaStandaloneSetup().createInjectorAndDoEMFRegistration(); @@ -188,13 +209,13 @@ private static void processRosettaFiles(List rosettaFiles, String tgtDir) List models = modelLoader.getRosettaModels(resources); if (models.isEmpty()) { LOGGER.error("No valid Rosetta models found."); - System.exit(1); + return 1; } String version = models.getFirst().getVersion(); LOGGER.info("Processing {} models, version: {}", models.size(), version); - IResourceValidator validator = injector.getInstance(IResourceValidator.class); + IResourceValidator validator = getValidator(injector); Map generatedPython = new HashMap<>(); List validModels = new ArrayList<>(); @@ -235,6 +256,9 @@ private static void processRosettaFiles(List rosettaFiles, String tgtDir) case WARNING: LOGGER.warn("Validation WARNING in {} (Line {}): {}", model.getName(), issue.getLineNumber(), issue.getMessage()); + if (failOnWarnings) { + hasErrors = true; + } break; default: break; @@ -246,24 +270,23 @@ private static void processRosettaFiles(List rosettaFiles, String tgtDir) continue; } - if (hasErrors) { - LOGGER.error("Skipping model {} due to validation errors.", model.getName()); + if (hasErrors && !allowErrors) { + LOGGER.error("Skipping model {} due to validation errors (allowErrors=false).", model.getName()); } else { + if (hasErrors) { + LOGGER.warn("Proceeding with model {} despite validation errors (allowErrors=true).", + model.getName()); + } validModels.add(model); } } if (validModels.isEmpty()) { LOGGER.error("No valid models found after validation. Exiting."); - System.exit(1); + return 1; } // Use validModels for generation - // Re-determine version based on valid models? Or keep original version? - // Assuming version is consistent across all loaded models or derived from the - // first one. - // The original code took version from models.getFirst().getVersion(); - LOGGER.info("Proceeding with generation for {} valid models.", validModels.size()); pythonCodeGenerator.beforeAllGenerate(resourceSet, validModels, version); @@ -276,6 +299,7 @@ private static void processRosettaFiles(List rosettaFiles, String tgtDir) generatedPython.putAll(pythonCodeGenerator.afterAllGenerate(resourceSet, models, version)); writePythonFiles(generatedPython, tgtDir); + return 0; } private static void writePythonFiles(Map generatedPython, String tgtDir) { @@ -356,4 +380,5 @@ public Injector createInjector() { return Guice.createInjector(new PythonRosettaRuntimeModule()); } } + } \ No newline at end of file diff --git a/src/test/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLITest.java b/src/test/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLITest.java new file mode 100644 index 00000000..7366ee3a --- /dev/null +++ b/src/test/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLITest.java @@ -0,0 +1,146 @@ +package com.regnosys.rosetta.generator.python; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.eclipse.emf.ecore.util.EcoreUtil; + +import static org.junit.jupiter.api.Assertions.*; + +class PythonCodeGeneratorCLITest { + + @TempDir + Path tempDir; + + @Test + void testMissingArgsReturnsError() { + PythonCodeGeneratorCLI cli = new PythonCodeGeneratorCLI(); + int exitCode = cli.execute(new String[] {}); + assertEquals(1, exitCode, "Should return 1 when no args provided"); + } + + @Test + void testHelpReturnsSuccess() { + PythonCodeGeneratorCLI cli = new PythonCodeGeneratorCLI(); + int exitCode = cli.execute(new String[] { "-h" }); + assertEquals(0, exitCode, "Should return 0 when help is requested"); + } + + @Test + void testInvalidSourceFileReturnsError() { + PythonCodeGeneratorCLI cli = new PythonCodeGeneratorCLI(); + int exitCode = cli.execute(new String[] { "-f", "non_existent_file.rosetta" }); + assertEquals(1, exitCode, "Should return 1 when source file does not exist"); + } + + @Test + void testValidationErrorsFailByDefault() throws IOException { + Path validFile = createValidRosettaFile(tempDir); // Use valid file, let MockValidator inject error + TestCLI cli = new TestCLI(); + cli.mockValidator.setReturnError(true); + + int exitCode = cli.execute(new String[] { + "-f", validFile.toString(), + "-t", tempDir.resolve("python").toString() + }); + + assertEquals(1, exitCode, "Should return 1 (fail) when validation errors occur by default"); + } + + @Test + void testAllowErrorsPasses() throws IOException { + Path validFile = createValidRosettaFile(tempDir); + TestCLI cli = new TestCLI(); + cli.mockValidator.setReturnError(true); + + int exitCode = cli.execute(new String[] { + "-f", validFile.toString(), + "-t", tempDir.resolve("python").toString(), + "-e" + }); + + assertEquals(0, exitCode, "Should return 0 (success) when validation errors occur but --allow-errors is set"); + } + + @Test + void testWarningsFailWithFlag() throws IOException { + Path validFile = createValidRosettaFile(tempDir); + TestCLI cli = new TestCLI(); + cli.mockValidator.setReturnWarning(true); + + int exitCode = cli.execute(new String[] { + "-f", validFile.toString(), + "-t", tempDir.resolve("python").toString(), + "-w" // --fail-on-warnings + }); + + assertEquals(1, exitCode, "Should return 1 (fail) when warnings occur and --fail-on-warnings is set"); + } + + private Path createValidRosettaFile(Path dir) throws IOException { + Path file = dir.resolve("valid.rosetta"); + String content = "namespace test.model\nversion \"1.0.0\"\ntype Foo:\n attr string (1..1)\n"; + Files.writeString(file, content); + return file; + } + + // --- Mocks --- + + static class TestCLI extends PythonCodeGeneratorCLI { + MockValidator mockValidator = new MockValidator(); + + @Override + protected org.eclipse.xtext.validation.IResourceValidator getValidator(com.google.inject.Injector injector) { + return mockValidator; + } + } + + static class MockValidator implements org.eclipse.xtext.validation.IResourceValidator { + private boolean returnError = false; + private boolean returnWarning = false; + + public void setReturnError(boolean returnError) { + this.returnError = returnError; + } + + public void setReturnWarning(boolean returnWarning) { + this.returnWarning = returnWarning; + } + + @Override + public java.util.List validate( + org.eclipse.emf.ecore.resource.Resource resource, + org.eclipse.xtext.validation.CheckMode mode, org.eclipse.xtext.util.CancelIndicator indicator) { + java.util.List issues = new java.util.ArrayList<>(); + + org.eclipse.emf.common.util.URI uri = null; + if (!resource.getContents().isEmpty()) { + org.eclipse.emf.ecore.EObject root = resource.getContents().get(0); + uri = EcoreUtil.getURI(root); + } else { + uri = org.eclipse.emf.common.util.URI.createURI("dummy#//"); + } + + if (returnError) { + issues.add(createIssue(org.eclipse.xtext.diagnostics.Severity.ERROR, "Mock Error", uri)); + } + if (returnWarning) { + issues.add(createIssue(org.eclipse.xtext.diagnostics.Severity.WARNING, "Mock Warning", uri)); + } + return issues; + } + + private org.eclipse.xtext.validation.Issue createIssue(org.eclipse.xtext.diagnostics.Severity severity, + String message, org.eclipse.emf.common.util.URI uri) { + org.eclipse.xtext.validation.Issue.IssueImpl issue = new org.eclipse.xtext.validation.Issue.IssueImpl(); + issue.setSeverity(severity); + issue.setMessage(message); + issue.setLineNumber(1); + issue.setUriToProblem(uri); + return issue; + } + } +} From ef951cf49f9a98942f2c4f78025d4140b82a73ba Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Feb 2026 19:31:51 +0000 Subject: [PATCH 11/25] fix(deps): update rosetta.dsl.version to v9.76.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a050f154..26fe3778 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ s01.oss.sonatype.org 20 - 9.76.1 + 9.76.2 2.38.0 6.0.0 From 086081be8047cf2c5ab762704a4f05f5d3fe4d45 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 10:01:33 +0000 Subject: [PATCH 12/25] fix(deps): update rosetta.dsl.version to v9.77.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 26fe3778..8fa7cd46 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ s01.oss.sonatype.org 20 - 9.76.2 + 9.77.0 2.38.0 6.0.0 From aa89132544b7566b8e99c1378bef37c8a9145735 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 15:15:37 +0000 Subject: [PATCH 13/25] fix(deps): update dependency org.jetbrains:annotations to v26.1.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8fa7cd46..fbde9371 100644 --- a/pom.xml +++ b/pom.xml @@ -294,7 +294,7 @@ org.jetbrains annotations - 26.0.2-1 + 26.1.0 compile From edd05ba5b6ccee502fa103967151967016d69244 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 21:17:25 +0000 Subject: [PATCH 14/25] fix(deps): update dependency org.junit:junit-bom to v6.0.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fbde9371..07771065 100644 --- a/pom.xml +++ b/pom.xml @@ -90,7 +90,7 @@ 1.5.21 - 6.0.2 + 6.0.3 3.0 From 712ec5be2379d663573de334dfbcfe0e8f697e95 Mon Sep 17 00:00:00 2001 From: dschwartznyc Date: Thu, 19 Feb 2026 18:08:01 -0500 Subject: [PATCH 15/25] fix: updated JUnit and Python Unit tests to not use Functions since those features have not been implemented yet --- .../python/PythonCodeGeneratorCLITest.java | 13 + .../RosettaOnlyExistsExpressionTest.java | 3 + .../expressions/RosettaShortcutTest.java | 2 + .../PythonFunctionAccumulationTest.java | 2 + .../functions/PythonFunctionAliasTest.java | 2 + .../functions/PythonFunctionBasicTest.java | 2 + .../PythonFunctionConditionTest.java | 2 + .../PythonFunctionControlFlowTest.java | 2 + .../functions/PythonFunctionOrderTest.java | 2 + .../functions/PythonFunctionTypeTest.java | 2 + .../rule/PythonDataRuleGeneratorTest.java | 3 + .../features/TestEnumUsage.rosetta | 14 +- .../features/collections/Collections.rosetta | 43 ++-- .../collections/ListExtensions.rosetta | 72 +++--- .../collections/test_list_extensions.py | 62 ++--- .../collections/test_list_operators.py | 16 +- .../expressions/ConditionalExpression.rosetta | 28 +- .../features/expressions/SwitchOp.rosetta | 17 +- .../expressions/TypeConversion.rosetta | 24 +- .../test_conditional_expression.py | 18 +- .../features/expressions/test_switch_op.py | 10 +- .../expressions/test_type_conversion.py | 24 +- .../features/functions/AddOperation.rosetta | 17 -- .../features/functions/FunctionTest.rosetta | 240 ------------------ .../features/functions/OrderTest.rosetta | 15 -- .../features/functions/test_functions_abs.py | 42 --- .../functions/test_functions_add_operation.py | 20 -- .../functions/test_functions_alias.py | 25 -- .../functions/test_functions_arithmetic.py | 12 - .../features/functions/test_functions_call.py | 6 - .../functions/test_functions_conditions.py | 24 -- .../functions/test_functions_metadata.py | 13 - .../test_functions_object_creation.py | 86 ------- .../functions/test_functions_order.py | 19 -- .../functions/test_local_conditions.py | 51 ---- .../features/language/test_enum_usage.py | 9 +- .../model_structure/Inheritance.rosetta | 9 +- .../model_structure/test_inheritance.py | 9 +- .../features/operators/ComparisonOp.rosetta | 46 +--- .../operators/ComplexBooleanLogic.rosetta | 30 +-- .../operators/test_comparison_operators.py | 50 ++-- .../operators/test_complex_boolean_logic.py | 33 ++- .../features/robustness/NullHandling.rosetta | 24 +- .../features/robustness/test_null_handling.py | 19 +- 44 files changed, 266 insertions(+), 896 deletions(-) delete mode 100644 test/python_unit_tests/features/functions/AddOperation.rosetta delete mode 100644 test/python_unit_tests/features/functions/FunctionTest.rosetta delete mode 100644 test/python_unit_tests/features/functions/OrderTest.rosetta delete mode 100644 test/python_unit_tests/features/functions/test_functions_abs.py delete mode 100644 test/python_unit_tests/features/functions/test_functions_add_operation.py delete mode 100644 test/python_unit_tests/features/functions/test_functions_alias.py delete mode 100644 test/python_unit_tests/features/functions/test_functions_arithmetic.py delete mode 100644 test/python_unit_tests/features/functions/test_functions_call.py delete mode 100644 test/python_unit_tests/features/functions/test_functions_conditions.py delete mode 100644 test/python_unit_tests/features/functions/test_functions_metadata.py delete mode 100644 test/python_unit_tests/features/functions/test_functions_object_creation.py delete mode 100644 test/python_unit_tests/features/functions/test_functions_order.py delete mode 100644 test/python_unit_tests/features/functions/test_local_conditions.py diff --git a/src/test/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLITest.java b/src/test/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLITest.java index 7366ee3a..5ff024a9 100644 --- a/src/test/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLITest.java +++ b/src/test/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLITest.java @@ -1,5 +1,7 @@ package com.regnosys.rosetta.generator.python; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -15,6 +17,17 @@ class PythonCodeGeneratorCLITest { @TempDir Path tempDir; + @BeforeAll + static void setup() { + System.out.println( + ">>> Starting PythonCodeGeneratorCLITest. Expected error and warning logs may follow as part of validation testing."); + } + + @AfterAll + static void tearDown() { + System.out.println(">>> Finished PythonCodeGeneratorCLITest."); + } + @Test void testMissingArgsReturnsError() { PythonCodeGeneratorCLI cli = new PythonCodeGeneratorCLI(); diff --git a/src/test/java/com/regnosys/rosetta/generator/python/expressions/RosettaOnlyExistsExpressionTest.java b/src/test/java/com/regnosys/rosetta/generator/python/expressions/RosettaOnlyExistsExpressionTest.java index 3c0923e1..4f0278f8 100644 --- a/src/test/java/com/regnosys/rosetta/generator/python/expressions/RosettaOnlyExistsExpressionTest.java +++ b/src/test/java/com/regnosys/rosetta/generator/python/expressions/RosettaOnlyExistsExpressionTest.java @@ -2,6 +2,7 @@ import org.eclipse.xtext.testing.InjectWith; import org.eclipse.xtext.testing.extensions.InjectionExtension; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -10,6 +11,7 @@ import jakarta.inject.Inject; +@Disabled("Functions are being phased out in tests.") @ExtendWith(InjectionExtension.class) @InjectWith(RosettaInjectorProvider.class) public class RosettaOnlyExistsExpressionTest { @@ -18,6 +20,7 @@ public class RosettaOnlyExistsExpressionTest { private PythonGeneratorTestUtils testUtils; @Test + @Disabled("Functions are being phased out in tests.") public void testOnlyExistsSinglePath() { testUtils.assertBundleContainsExpectedString(""" type A: diff --git a/src/test/java/com/regnosys/rosetta/generator/python/expressions/RosettaShortcutTest.java b/src/test/java/com/regnosys/rosetta/generator/python/expressions/RosettaShortcutTest.java index 2aa79516..b6d9eba8 100644 --- a/src/test/java/com/regnosys/rosetta/generator/python/expressions/RosettaShortcutTest.java +++ b/src/test/java/com/regnosys/rosetta/generator/python/expressions/RosettaShortcutTest.java @@ -2,6 +2,7 @@ import org.eclipse.xtext.testing.InjectWith; import org.eclipse.xtext.testing.extensions.InjectionExtension; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -10,6 +11,7 @@ import jakarta.inject.Inject; +@Disabled("Functions are being phased out in tests.") @ExtendWith(InjectionExtension.class) @InjectWith(RosettaInjectorProvider.class) public class RosettaShortcutTest { diff --git a/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionAccumulationTest.java b/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionAccumulationTest.java index ddb95a87..9f3cc568 100644 --- a/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionAccumulationTest.java +++ b/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionAccumulationTest.java @@ -4,11 +4,13 @@ import com.regnosys.rosetta.tests.RosettaInjectorProvider; import org.eclipse.xtext.testing.InjectWith; import org.eclipse.xtext.testing.extensions.InjectionExtension; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import jakarta.inject.Inject; import java.util.Map; +@Disabled("Functions are being phased out in tests.") @ExtendWith(InjectionExtension.class) @InjectWith(RosettaInjectorProvider.class) public class PythonFunctionAccumulationTest { diff --git a/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionAliasTest.java b/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionAliasTest.java index cd74b7da..636312ba 100644 --- a/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionAliasTest.java +++ b/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionAliasTest.java @@ -4,11 +4,13 @@ import com.regnosys.rosetta.tests.RosettaInjectorProvider; import org.eclipse.xtext.testing.InjectWith; import org.eclipse.xtext.testing.extensions.InjectionExtension; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import jakarta.inject.Inject; import java.util.Map; +@Disabled("Functions are being phased out in tests.") @ExtendWith(InjectionExtension.class) @InjectWith(RosettaInjectorProvider.class) public class PythonFunctionAliasTest { diff --git a/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionBasicTest.java b/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionBasicTest.java index af6302c6..7a67d1cd 100644 --- a/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionBasicTest.java +++ b/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionBasicTest.java @@ -4,11 +4,13 @@ import com.regnosys.rosetta.tests.RosettaInjectorProvider; import org.eclipse.xtext.testing.InjectWith; import org.eclipse.xtext.testing.extensions.InjectionExtension; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import jakarta.inject.Inject; import java.util.Map; +@Disabled("Functions are being phased out in tests.") @ExtendWith(InjectionExtension.class) @InjectWith(RosettaInjectorProvider.class) public class PythonFunctionBasicTest { diff --git a/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionConditionTest.java b/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionConditionTest.java index a1dcef35..8b78aeaa 100644 --- a/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionConditionTest.java +++ b/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionConditionTest.java @@ -4,11 +4,13 @@ import com.regnosys.rosetta.tests.RosettaInjectorProvider; import org.eclipse.xtext.testing.InjectWith; import org.eclipse.xtext.testing.extensions.InjectionExtension; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import jakarta.inject.Inject; import java.util.Map; +@Disabled("Functions are being phased out in tests.") @ExtendWith(InjectionExtension.class) @InjectWith(RosettaInjectorProvider.class) public class PythonFunctionConditionTest { diff --git a/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionControlFlowTest.java b/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionControlFlowTest.java index efe25c77..1ca81c50 100644 --- a/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionControlFlowTest.java +++ b/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionControlFlowTest.java @@ -4,11 +4,13 @@ import com.regnosys.rosetta.tests.RosettaInjectorProvider; import org.eclipse.xtext.testing.InjectWith; import org.eclipse.xtext.testing.extensions.InjectionExtension; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import jakarta.inject.Inject; import java.util.Map; +@Disabled("Functions are being phased out in tests.") @ExtendWith(InjectionExtension.class) @InjectWith(RosettaInjectorProvider.class) public class PythonFunctionControlFlowTest { diff --git a/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionOrderTest.java b/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionOrderTest.java index c6a6c2b7..1142e216 100644 --- a/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionOrderTest.java +++ b/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionOrderTest.java @@ -4,6 +4,7 @@ import com.regnosys.rosetta.tests.RosettaInjectorProvider; import org.eclipse.xtext.testing.InjectWith; import org.eclipse.xtext.testing.extensions.InjectionExtension; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import jakarta.inject.Inject; @@ -11,6 +12,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Disabled; +@Disabled("Functions are being phased out in tests.") @ExtendWith(InjectionExtension.class) @InjectWith(RosettaInjectorProvider.class) public class PythonFunctionOrderTest { diff --git a/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionTypeTest.java b/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionTypeTest.java index 28c74b93..c8f0111a 100644 --- a/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionTypeTest.java +++ b/src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionTypeTest.java @@ -5,11 +5,13 @@ import org.eclipse.xtext.testing.InjectWith; import org.eclipse.xtext.testing.extensions.InjectionExtension; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import jakarta.inject.Inject; import java.util.Map; +@Disabled("Functions are being phased out in tests.") @ExtendWith(InjectionExtension.class) @InjectWith(RosettaInjectorProvider.class) public class PythonFunctionTypeTest { diff --git a/src/test/java/com/regnosys/rosetta/generator/python/rule/PythonDataRuleGeneratorTest.java b/src/test/java/com/regnosys/rosetta/generator/python/rule/PythonDataRuleGeneratorTest.java index 0cb5c25e..1d9358f9 100644 --- a/src/test/java/com/regnosys/rosetta/generator/python/rule/PythonDataRuleGeneratorTest.java +++ b/src/test/java/com/regnosys/rosetta/generator/python/rule/PythonDataRuleGeneratorTest.java @@ -5,6 +5,7 @@ import com.regnosys.rosetta.generator.python.PythonGeneratorTestUtils; import org.eclipse.xtext.testing.InjectWith; import org.eclipse.xtext.testing.extensions.InjectionExtension; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -226,6 +227,7 @@ class com_rosetta_test_model_QuotePrice(BaseDataClass): } @Test + @Disabled("Functions are being phased out in tests.") public void dataRuleWithDoIfAndFunction() { String pythonString = testUtils.generatePythonFromString( """ @@ -263,6 +265,7 @@ def _else_fn0(): } @Test + @Disabled("Functions are being phased out in tests.") public void dataRuleWithDoIfAndFunctionAndElse() { String pythonString = testUtils.generatePythonFromString( """ diff --git a/test/python_unit_tests/features/TestEnumUsage.rosetta b/test/python_unit_tests/features/TestEnumUsage.rosetta index 572a7693..daead854 100644 --- a/test/python_unit_tests/features/TestEnumUsage.rosetta +++ b/test/python_unit_tests/features/TestEnumUsage.rosetta @@ -8,12 +8,10 @@ enum TrafficLight: type TrafficLightType: val TrafficLight(1..1) -func CheckLight: - inputs: - color TrafficLight(1..1) - output: - res string(1..1) - set res: - if color = TrafficLight -> Red +type CheckLightTest: + color TrafficLight(1..1) + target string(1..1) + condition TestCond: + (if color = TrafficLight -> Red then "Stop" - else "Go" + else "Go") = target diff --git a/test/python_unit_tests/features/collections/Collections.rosetta b/test/python_unit_tests/features/collections/Collections.rosetta index 319586ac..35aaa174 100644 --- a/test/python_unit_tests/features/collections/Collections.rosetta +++ b/test/python_unit_tests/features/collections/Collections.rosetta @@ -54,15 +54,23 @@ type SortTest: then True else False -func JoinTestFunction: <"Test join operation"> - inputs: - field1 string (1..1) - field2 string (1..1) - delimiter string (1..1) - output: - result string (1..1) - set result: - [field1, field2] join delimiter +type JoinTest: + field1 string (1..1) + field2 string (1..1) + delimiter string (1..1) + target string (1..1) + condition TestCond: + if ([field1, field2] join delimiter) = target + then True + else False + +type FlattenTest: + fc FlattenContainer (1..*) + target int (0..*) + condition TestCond: + if (fc extract items then extract items then flatten) all = target + then True + else False type FilterItem: fi int (1..1) @@ -81,23 +89,12 @@ type FlattenBar: type FlattenFoo: bars FlattenBar (0..*) condition TestCondFoo: - [1, 2, 3] = (bars - extract numbers - then flatten) + if [1, 2, 3] all = (bars extract numbers then flatten) + then True + else False type FlattenItem: items int (1..*) type FlattenContainer: items FlattenItem (1..*) - -func FlattenTestFunction: <"Test flatten operation"> - inputs: - fc FlattenContainer (1..*) <"Test value"> - output: - result int (1..*) - set result: - fc - extract items - then extract items - then flatten diff --git a/test/python_unit_tests/features/collections/ListExtensions.rosetta b/test/python_unit_tests/features/collections/ListExtensions.rosetta index a3edab1c..cd67e9a5 100644 --- a/test/python_unit_tests/features/collections/ListExtensions.rosetta +++ b/test/python_unit_tests/features/collections/ListExtensions.rosetta @@ -1,49 +1,37 @@ namespace rosetta_dsl.test.semantic.collections.extensions : <"generate Python unit tests from Rosetta."> -func ListFirst: - inputs: - items int(0..*) - output: - res int(0..1) - set res: - items first +type ListFirstTest: + items int (0..*) + target int (0..1) + condition TestCond: + items first = target -func ListLast: - inputs: - items int(0..*) - output: - res int(0..1) - set res: - items last +type ListLastTest: + items int (0..*) + target int (0..1) + condition TestCond: + items last = target -func ListDistinct: - inputs: - items int(0..*) - output: - res int(0..*) - set res: - items distinct +type ListDistinctTest: + items int (0..*) + target int (0..*) + condition TestCond: + (items distinct) all = (target distinct) -func ListSum: - inputs: - items int(0..*) - output: - res int(1..1) - set res: - items sum +type ListSumTest: + items int (0..*) + target int (1..1) + condition TestCond: + items sum = target -func ListOnlyElement: - inputs: - items int(0..*) - output: - res int(0..1) - set res: - items only-element +type ListOnlyElementTest: + items int (0..*) + target int (0..1) + condition TestCond: + items only-element = target -func ListReverse: - inputs: - items int(0..*) - output: - res int(0..*) - set res: - items reverse +type ListReverseTest: + items int (0..*) + target int (0..*) + condition TestCond: + (items reverse) all = target diff --git a/test/python_unit_tests/features/collections/test_list_extensions.py b/test/python_unit_tests/features/collections/test_list_extensions.py index b226bba9..47435b1f 100644 --- a/test/python_unit_tests/features/collections/test_list_extensions.py +++ b/test/python_unit_tests/features/collections/test_list_extensions.py @@ -1,71 +1,61 @@ """List extensions unit tests""" -from rosetta_dsl.test.semantic.collections.extensions.functions.ListFirst import ( - ListFirst, +import pytest +from rosetta_dsl.test.semantic.collections.extensions.ListFirstTest import ( + ListFirstTest, ) -from rosetta_dsl.test.semantic.collections.extensions.functions.ListLast import ListLast -from rosetta_dsl.test.semantic.collections.extensions.functions.ListDistinct import ( - ListDistinct, +from rosetta_dsl.test.semantic.collections.extensions.ListLastTest import ListLastTest +from rosetta_dsl.test.semantic.collections.extensions.ListDistinctTest import ( + ListDistinctTest, ) -from rosetta_dsl.test.semantic.collections.extensions.functions.ListSum import ListSum -from rosetta_dsl.test.semantic.collections.extensions.functions.ListOnlyElement import ( - ListOnlyElement, +from rosetta_dsl.test.semantic.collections.extensions.ListSumTest import ListSumTest +from rosetta_dsl.test.semantic.collections.extensions.ListOnlyElementTest import ( + ListOnlyElementTest, ) -from rosetta_dsl.test.semantic.collections.extensions.functions.ListReverse import ( - ListReverse, +from rosetta_dsl.test.semantic.collections.extensions.ListReverseTest import ( + ListReverseTest, ) def test_list_first(): """Test 'first' list operator.""" - assert ListFirst(items=[1, 2, 3]) == 1 + ListFirstTest(items=[1, 2, 3], target=1).validate_model() # Current implementation raises IndexError for empty list - try: - ListFirst(items=[]) - except IndexError: - pass + with pytest.raises(Exception): + ListFirstTest(items=[], target=None).validate_model() def test_list_last(): """Test 'last' list operator.""" - assert ListLast(items=[1, 2, 3]) == 3 + ListLastTest(items=[1, 2, 3], target=3).validate_model() # Current implementation raises IndexError for empty list - try: - ListLast(items=[]) - except IndexError: - pass + with pytest.raises(Exception): + ListLastTest(items=[], target=None).validate_model() def test_list_distinct(): """Test 'distinct' list operator.""" - res = ListDistinct(items=[1, 2, 2, 3]) - # distinct works - assert len(res) == 3 - assert 1 in res + ListDistinctTest(items=[1, 2, 2, 3], target=[1, 2, 3]).validate_model() def test_list_sum(): """Test 'sum' list operator.""" - assert ListSum(items=[1, 2, 3]) == 6 - assert ListSum(items=[]) == 0 + ListSumTest(items=[1, 2, 3], target=6).validate_model() + ListSumTest(items=[], target=0).validate_model() def test_list_only_element(): """Test 'only-element' list operator.""" - assert ListOnlyElement(items=[1]) == 1 + ListOnlyElementTest(items=[1], target=1).validate_model() # Returns None if multiple elements exist - assert ListOnlyElement(items=[1, 2]) is None + ListOnlyElementTest(items=[1, 2], target=None).validate_model() - # Returns None or raises IndexError if empty? - try: - val = ListOnlyElement(items=[]) - assert val is None - except IndexError: - pass + # Returns None if empty + ListOnlyElementTest(items=[], target=None).validate_model() def test_list_reverse(): """Test 'reverse' list operator.""" - assert ListReverse(items=[1, 2, 3]) == [3, 2, 1] - assert ListReverse(items=[]) == [] + ListReverseTest(items=[1, 2, 3], target=[3, 2, 1]).validate_model() + ListReverseTest(items=[], target=[]).validate_model() diff --git a/test/python_unit_tests/features/collections/test_list_operators.py b/test/python_unit_tests/features/collections/test_list_operators.py index 30c8967e..eea3168d 100644 --- a/test/python_unit_tests/features/collections/test_list_operators.py +++ b/test/python_unit_tests/features/collections/test_list_operators.py @@ -10,14 +10,10 @@ from rosetta_dsl.test.semantic.collections.MaxTest import MaxTest from rosetta_dsl.test.semantic.collections.LastTest import LastTest from rosetta_dsl.test.semantic.collections.SortTest import SortTest -from rosetta_dsl.test.semantic.collections.functions.JoinTestFunction import ( - JoinTestFunction, -) +from rosetta_dsl.test.semantic.collections.JoinTest import JoinTest from rosetta_dsl.test.semantic.collections.FlattenItem import FlattenItem from rosetta_dsl.test.semantic.collections.FlattenContainer import FlattenContainer -from rosetta_dsl.test.semantic.collections.functions.FlattenTestFunction import ( - FlattenTestFunction, -) +from rosetta_dsl.test.semantic.collections.FlattenTest import FlattenTest from rosetta_dsl.test.semantic.collections.FlattenBar import FlattenBar from rosetta_dsl.test.semantic.collections.FlattenFoo import FlattenFoo from rosetta_dsl.test.semantic.collections.FilterItem import FilterItem @@ -78,8 +74,7 @@ def test_sort_passes(): def test_join_passes(): """join tests passes""" - join_test = JoinTestFunction(field1="a", field2="b", delimiter="") - assert join_test == "ab" + JoinTest(field1="a", field2="b", delimiter="", target="ab").validate_model() def test_flatten_passes(): @@ -88,8 +83,9 @@ def test_flatten_passes(): flatten_container = FlattenContainer( items=[flatten_item, flatten_item, flatten_item] ) - result = FlattenTestFunction(fc=[flatten_container]) - assert result == [1, 2, 3, 1, 2, 3, 1, 2, 3] + FlattenTest( + fc=[flatten_container], target=[1, 2, 3, 1, 2, 3, 1, 2, 3] + ).validate_model() def test_flatten_foo_passes(): diff --git a/test/python_unit_tests/features/expressions/ConditionalExpression.rosetta b/test/python_unit_tests/features/expressions/ConditionalExpression.rosetta index 21745471..ef1b933c 100644 --- a/test/python_unit_tests/features/expressions/ConditionalExpression.rosetta +++ b/test/python_unit_tests/features/expressions/ConditionalExpression.rosetta @@ -1,23 +1,19 @@ namespace rosetta_dsl.test.semantic.expressions.conditional : <"generate Python unit tests from Rosetta."> -func ConditionalValue: - inputs: - param int(1..1) - output: - res string(1..1) - set res: - if param > 10 +type ConditionalValueTest: + param int(1..1) + target string(1..1) + condition TestCond: + (if param > 10 then "High" - else "Low" + else "Low") = target -func ConditionalNested: - inputs: - param int(1..1) - output: - res string(1..1) - set res: - if param > 10 +type ConditionalNestedTest: + param int(1..1) + target string(1..1) + condition TestCond: + (if param > 10 then "High" else if param > 5 then "Medium" - else "Low" + else "Low") = target diff --git a/test/python_unit_tests/features/expressions/SwitchOp.rosetta b/test/python_unit_tests/features/expressions/SwitchOp.rosetta index c3109fb7..f8a2cd0b 100644 --- a/test/python_unit_tests/features/expressions/SwitchOp.rosetta +++ b/test/python_unit_tests/features/expressions/SwitchOp.rosetta @@ -1,13 +1,12 @@ namespace rosetta_dsl.test.semantic.expressions.switch_op -func SwitchTest: <"Test switch operation"> - inputs: - x int (1..1) - output: - res string (1..1) - - set res: - x switch +type SwitchTest: <"Test switch operation"> + x int (1..1) + target string (1..1) + condition TestCond: + if (x switch 1 then "One", 2 then "Two", - default "Other" + default "Other") = target + then True + else False diff --git a/test/python_unit_tests/features/expressions/TypeConversion.rosetta b/test/python_unit_tests/features/expressions/TypeConversion.rosetta index 924df359..ac5eb41a 100644 --- a/test/python_unit_tests/features/expressions/TypeConversion.rosetta +++ b/test/python_unit_tests/features/expressions/TypeConversion.rosetta @@ -1,17 +1,13 @@ namespace rosetta_dsl.test.semantic.expressions.type_conversion : <"generate Python unit tests from Rosetta."> -func StringToInt: - inputs: - s string(1..1) - output: - res int(1..1) - set res: - s to-int +type StringToIntTest: + s string(1..1) + target int(1..1) + condition TestCond: + (s to-int) = target -func IntToString: - inputs: - i int(1..1) - output: - res string(1..1) - set res: - i to-string +type IntToStringTest: + i int(1..1) + target string(1..1) + condition TestCond: + (i to-string) = target diff --git a/test/python_unit_tests/features/expressions/test_conditional_expression.py b/test/python_unit_tests/features/expressions/test_conditional_expression.py index 1edd6dcc..15e00431 100644 --- a/test/python_unit_tests/features/expressions/test_conditional_expression.py +++ b/test/python_unit_tests/features/expressions/test_conditional_expression.py @@ -1,24 +1,24 @@ """Conditional expression unit tests""" -from rosetta_dsl.test.semantic.expressions.conditional.functions.ConditionalValue import ( - ConditionalValue, +from rosetta_dsl.test.semantic.expressions.conditional.ConditionalValueTest import ( + ConditionalValueTest, ) -from rosetta_dsl.test.semantic.expressions.conditional.functions.ConditionalNested import ( - ConditionalNested, +from rosetta_dsl.test.semantic.expressions.conditional.ConditionalNestedTest import ( + ConditionalNestedTest, ) def test_conditional_value(): """Test simple if-then-else expression.""" - assert ConditionalValue(param=20) == "High" - assert ConditionalValue(param=5) == "Low" + ConditionalValueTest(param=20, target="High").validate_model() + ConditionalValueTest(param=5, target="Low").validate_model() def test_conditional_nested(): """Test nested if-then-else expression.""" - assert ConditionalNested(param=20) == "High" - assert ConditionalNested(param=8) == "Medium" - assert ConditionalNested(param=2) == "Low" + ConditionalNestedTest(param=20, target="High").validate_model() + ConditionalNestedTest(param=8, target="Medium").validate_model() + ConditionalNestedTest(param=2, target="Low").validate_model() if __name__ == "__main__": diff --git a/test/python_unit_tests/features/expressions/test_switch_op.py b/test/python_unit_tests/features/expressions/test_switch_op.py index 85156700..c5eb14ae 100644 --- a/test/python_unit_tests/features/expressions/test_switch_op.py +++ b/test/python_unit_tests/features/expressions/test_switch_op.py @@ -1,18 +1,16 @@ """Switch expression unit tests""" -from rosetta_dsl.test.semantic.expressions.switch_op.functions.SwitchTest import ( - SwitchTest, -) +from rosetta_dsl.test.semantic.expressions.switch_op.SwitchTest import SwitchTest def test_switch_op(): """Test switch operation.""" # Test valid cases - assert SwitchTest(x=1) == "One" - assert SwitchTest(x=2) == "Two" + SwitchTest(x=1, target="One").validate_model() + SwitchTest(x=2, target="Two").validate_model() # Test default case - assert SwitchTest(x=3) == "Other" + SwitchTest(x=3, target="Other").validate_model() if __name__ == "__main__": diff --git a/test/python_unit_tests/features/expressions/test_type_conversion.py b/test/python_unit_tests/features/expressions/test_type_conversion.py index 2a708126..50a4c85c 100644 --- a/test/python_unit_tests/features/expressions/test_type_conversion.py +++ b/test/python_unit_tests/features/expressions/test_type_conversion.py @@ -1,26 +1,18 @@ """Type conversion unit tests""" -import pytest -from rosetta_dsl.test.semantic.expressions.type_conversion.functions.StringToInt import ( - StringToInt, +from rosetta_dsl.test.semantic.expressions.type_conversion.StringToIntTest import ( + StringToIntTest, ) -from rosetta_dsl.test.semantic.expressions.type_conversion.functions.IntToString import ( - IntToString, +from rosetta_dsl.test.semantic.expressions.type_conversion.IntToStringTest import ( + IntToStringTest, ) def test_string_to_int(): - """Test string to integer conversion.""" - assert StringToInt(s="123") == 123 - with pytest.raises(Exception): # ValueError or similar - StringToInt(s="abc") + """Test 'to-int' conversion.""" + StringToIntTest(s="123", target=123).validate_model() def test_int_to_string(): - """Test integer to string conversion.""" - assert IntToString(i=456) == "456" - - -if __name__ == "__main__": - test_string_to_int() - test_int_to_string() + """Test 'to-string' conversion.""" + IntToStringTest(i=456, target="456").validate_model() diff --git a/test/python_unit_tests/features/functions/AddOperation.rosetta b/test/python_unit_tests/features/functions/AddOperation.rosetta deleted file mode 100644 index a64dddb8..00000000 --- a/test/python_unit_tests/features/functions/AddOperation.rosetta +++ /dev/null @@ -1,17 +0,0 @@ -namespace rosetta_dsl.test.functions.add_operation : <"generate Python unit tests from Rosetta."> - -type UnitType: - currency string (0..1) - -type Quantity: - value number (0..1) - unit UnitType (0..1) - -func FilterQuantity: - inputs: - quantities Quantity (0..*) - unit UnitType (1..1) - output: - filteredQuantities Quantity (0..*) - - add filteredQuantities: quantities filter item -> unit = unit diff --git a/test/python_unit_tests/features/functions/FunctionTest.rosetta b/test/python_unit_tests/features/functions/FunctionTest.rosetta deleted file mode 100644 index ac9f0936..00000000 --- a/test/python_unit_tests/features/functions/FunctionTest.rosetta +++ /dev/null @@ -1,240 +0,0 @@ -namespace rosetta_dsl.test.functions : <"generate Python unit tests from Rosetta."> - -func TestAbsNumber: <"Returns the absolute value of a number. If the argument is not negative, the argument is returned. If the argument is negative, the negation of the argument is returned."> - inputs: - arg number (1..1) - output: - result number (1..1) - set result: if arg < 0 then -1 * arg else arg - -type AInput: <"A type"> - a number (1..1) - -func TestAbsInputType: <"Returns the absolute value of a number. If the argument is not negative, the argument is returned. If the argument is negative, the negation of the argument is returned."> - inputs: - arg AInput (1..1) - output: - result number (1..1) - set result: if arg -> a < 0 then -1 * arg -> a else arg -> a - -type AOutput: <"A type"> - a number (1..1) - -func TestAbsOutputType: <"Returns the absolute value of a number. If the argument is not negative, the argument is returned. If the argument is negative, the negation of the argument is returned."> - inputs: - arg number (1..1) - output: - result AOutput (1..1) - set result: - AOutput { - a: if arg < 0 then arg * -1 else arg - } - -enum ArithmeticOperationEnum: <"An arithmetic operator that can be passed to a function"> - Add <"Addition"> - Subtract <"Subtraction"> - Multiply <"Multiplication"> - Divide <"Division"> - Max <"Max of 2 values"> - Min <"Min of 2 values"> - -func ArithmeticOperation: - inputs: - n1 number (1..1) - op ArithmeticOperationEnum (1..1) - n2 number (1..1) - output: - result number (1..1) - - set result: - if op = ArithmeticOperationEnum -> Add then - n1 + n2 - else if op = ArithmeticOperationEnum -> Subtract then - n1 - n2 - else if op = ArithmeticOperationEnum -> Multiply then - n1 * n2 - else if op = ArithmeticOperationEnum -> Divide then - n1 / n2 - else if op = ArithmeticOperationEnum -> Max then - [n1, n2] max - else if op = ArithmeticOperationEnum -> Min then - [n1, n2] min - -func TestAlias: - inputs: - inp1 number(1..1) - inp2 number(1..1) - output: - result number(1..1) - alias Alias: - if inp1 < inp2 then inp1 else inp2 - - set result: - Alias - -type ComplexTypeA: - valueA number(1..1) - -type ComplexTypeB: - valueB number(1..1) - -type ComplexTypeC: - valueA number(1..1) - valueB number(1..1) - -func TestComplexTypeInputs: - inputs: - a ComplexTypeA (1..1) - b ComplexTypeB (1..1) - output: - c ComplexTypeC (1..1) - set c->valueA: - a->valueA - set c->valueB: - b->valueB - -type A: - valueA number(1..1) - -type B: - valueB number(1..1) - -type C: - valueC number(1..1) - -func TestAliasWithBaseModelInputs: - inputs: - a A (1..1) - b B (1..1) - output: - c C (1..1) - alias Alias1: - a->valueA - alias Alias2: - b->valueB -set c: - C { - valueC: Alias1 * Alias2 - } -func MinMaxWithSimpleCondition: - inputs: - in1 number (1..1) - in2 number (1..1) - direction string (1..1) - output: - result number (1..1) - condition Directiom: - direction = "min" or direction = "max" - set result: - if direction = "min" then - [in1, in2] min - else if direction = "max" then - [in1, in2] max - -func MinMaxWithPostCondition: - inputs: - in1 number (1..1) - in2 number (1..1) - direction string (1..1) - output: - result number (1..1) - set result: - if direction = "min" then - [in1, in2] min - else if direction = "max" then - [in1, in2] max - post-condition Directiom: - direction = "min" or direction = "max" - -func BaseFunction: - inputs: - value number (1..1) - output: - result number (1..1) - set result: - value * 2 - -func MainFunction: - inputs: - value number (1..1) - output: - result number (1..1) - set result: - BaseFunction(value) - -type KeyEntity: - [metadata key] - value int (1..1) - -type RefEntity: - ke KeyEntity (1..1) - [metadata reference] - -func MetadataFunction: - inputs: - ref RefEntity (1..1) - output: - result int (1..1) - set result: - ref->ke->value - -type BaseObject: - value1 int (1..1) - value2 int (1..1) - -type BaseObjectWithBaseClassFields: - value1 int (1..1) - value2 int (1..1) - strict boolean (1..1) - -func TestSimpleObjectAssignment: - inputs: - baseObject BaseObject (1..1) - output: - result BaseObject (1..1) - set result: - baseObject - -func TestObjectCreationFromFields: - inputs: - baseObject BaseObject (1..1) - output: - result BaseObject (1..1) - set result: - BaseObject { - value1: baseObject->value1, - value2: baseObject->value2 - } - -type ContainerObject: - baseObject BaseObject (1..1) - value3 int (1..1) - -func TestContainerObjectCreation: - inputs: - value1 int (1..1) - value2 int (1..1) - value3 int (1..1) - output: - result ContainerObject (1..1) - set result: - ContainerObject { - baseObject: BaseObject { - value1: value1, - value2: value2 - }, - value3: value3 - } - -func TestContainerObjectCreationFromBaseObject: - inputs: - baseObject BaseObject (1..1) - value3 int (1..1) - output: - result ContainerObject (1..1) - set result: - ContainerObject { - baseObject: baseObject, - value3: value3 - } - \ No newline at end of file diff --git a/test/python_unit_tests/features/functions/OrderTest.rosetta b/test/python_unit_tests/features/functions/OrderTest.rosetta deleted file mode 100644 index 29d66b4d..00000000 --- a/test/python_unit_tests/features/functions/OrderTest.rosetta +++ /dev/null @@ -1,15 +0,0 @@ -namespace rosetta_dsl.test.functions.order - -type ClassB: - attr ClassA (1..1) - -func MyFunc: - inputs: - arg ClassB (1..1) - output: - out ClassA (1..1) - set out: - arg->attr - -type ClassA: - val string (1..1) diff --git a/test/python_unit_tests/features/functions/test_functions_abs.py b/test/python_unit_tests/features/functions/test_functions_abs.py deleted file mode 100644 index 6c724884..00000000 --- a/test/python_unit_tests/features/functions/test_functions_abs.py +++ /dev/null @@ -1,42 +0,0 @@ -from rosetta_dsl.test.functions.functions.TestAbsNumber import TestAbsNumber -from rosetta_dsl.test.functions.AInput import AInput -from rosetta_dsl.test.functions.functions.TestAbsInputType import TestAbsInputType -from rosetta_dsl.test.functions.functions.TestAbsOutputType import TestAbsOutputType - - -def test_abs_positive(): - """Test abs positive""" - result = TestAbsNumber(arg=5) - assert result == 5 - - -def test_abs_negative(): - """Test abs negative""" - result = TestAbsNumber(arg=-5) - assert result == 5 - - -def test_abs_input_type_positive(): - """Test abs type positive""" - a = AInput(a=5) - result = TestAbsInputType(arg=a) - assert result == 5 - - -def test_abs_input_type_negative(): - """Test abs type negative""" - a = AInput(a=-5) - result = TestAbsInputType(arg=a) - assert result == 5 - - -def test_abs_output_type_positive(): - """Test abs output type positive""" - result = TestAbsOutputType(arg=5) - assert result.a == 5 - - -def test_abs_output_type_negative(): - """Test abs output type negative""" - result = TestAbsOutputType(arg=-5) - assert result.a == 5 diff --git a/test/python_unit_tests/features/functions/test_functions_add_operation.py b/test/python_unit_tests/features/functions/test_functions_add_operation.py deleted file mode 100644 index bd4737a3..00000000 --- a/test/python_unit_tests/features/functions/test_functions_add_operation.py +++ /dev/null @@ -1,20 +0,0 @@ -from rosetta_dsl.test.functions.add_operation.UnitType import UnitType -from rosetta_dsl.test.functions.add_operation.Quantity import Quantity -from rosetta_dsl.test.functions.add_operation.functions.FilterQuantity import ( - FilterQuantity, -) - - -def test_add_operation(): - """Test add operation""" - fx_eur = UnitType(currency="EUR") - fx_jpy = UnitType(currency="JPY") - fx_usd = UnitType(currency="USD") - list_of_quantities = [ - Quantity(unit=fx_eur), - Quantity(unit=fx_jpy), - Quantity(unit=fx_usd), - ] - fq = FilterQuantity(quantities=list_of_quantities, unit=fx_jpy) - assert len(fq) == 1 - assert fq[0].unit.currency == "JPY" diff --git a/test/python_unit_tests/features/functions/test_functions_alias.py b/test/python_unit_tests/features/functions/test_functions_alias.py deleted file mode 100644 index 47eef5c1..00000000 --- a/test/python_unit_tests/features/functions/test_functions_alias.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -Unit tests for functions. -""" - -from rosetta_dsl.test.functions.functions.TestAlias import TestAlias -from rosetta_dsl.test.functions.functions.TestAliasWithBaseModelInputs import ( - TestAliasWithBaseModelInputs, -) -from rosetta_dsl.test.functions.A import A -from rosetta_dsl.test.functions.B import B - - -def test_alias(): - """Test alias""" - assert TestAlias(inp1=5, inp2=10) == 5 - assert TestAlias(inp1=10, inp2=5) == 5 - - -def test_alias_with_base_model_inputs(): - """Test alias with base model inputs""" - a = A(valueA=5) - b = B(valueB=10) - c = TestAliasWithBaseModelInputs(a=a, b=b) - print(c) - assert c.valueC == 50 diff --git a/test/python_unit_tests/features/functions/test_functions_arithmetic.py b/test/python_unit_tests/features/functions/test_functions_arithmetic.py deleted file mode 100644 index 73a3ef18..00000000 --- a/test/python_unit_tests/features/functions/test_functions_arithmetic.py +++ /dev/null @@ -1,12 +0,0 @@ -from rosetta_dsl.test.functions.functions.ArithmeticOperation import ArithmeticOperation -from rosetta_dsl.test.functions.ArithmeticOperationEnum import ArithmeticOperationEnum - - -def test_arithmetic_operation(): - """Test arithmetic operation""" - assert ArithmeticOperation(n1=5, op=ArithmeticOperationEnum.ADD, n2=10) == 15 - assert ArithmeticOperation(n1=5, op=ArithmeticOperationEnum.SUBTRACT, n2=10) == -5 - assert ArithmeticOperation(n1=5, op=ArithmeticOperationEnum.MULTIPLY, n2=10) == 50 - assert ArithmeticOperation(n1=5, op=ArithmeticOperationEnum.DIVIDE, n2=10) == 0.5 - assert ArithmeticOperation(n1=5, op=ArithmeticOperationEnum.MAX, n2=10) == 10 - assert ArithmeticOperation(n1=5, op=ArithmeticOperationEnum.MIN, n2=10) == 5 diff --git a/test/python_unit_tests/features/functions/test_functions_call.py b/test/python_unit_tests/features/functions/test_functions_call.py deleted file mode 100644 index 0506d67b..00000000 --- a/test/python_unit_tests/features/functions/test_functions_call.py +++ /dev/null @@ -1,6 +0,0 @@ -from rosetta_dsl.test.functions.functions.MainFunction import MainFunction - - -def test_function_with_function_call(): - """Test function with function call""" - assert MainFunction(value=5) == 10 diff --git a/test/python_unit_tests/features/functions/test_functions_conditions.py b/test/python_unit_tests/features/functions/test_functions_conditions.py deleted file mode 100644 index 5ce0bb49..00000000 --- a/test/python_unit_tests/features/functions/test_functions_conditions.py +++ /dev/null @@ -1,24 +0,0 @@ -import pytest -from rune.runtime.conditions import ConditionViolationError -from rosetta_dsl.test.functions.functions.MinMaxWithSimpleCondition import ( - MinMaxWithSimpleCondition, -) -from rosetta_dsl.test.functions.functions.MinMaxWithPostCondition import ( - MinMaxWithPostCondition, -) - - -def test_min_max_simple_conditions(): - """Test min max simple conditions""" - assert MinMaxWithSimpleCondition(in1=5, in2=10, direction="min") == 5 - assert MinMaxWithSimpleCondition(in1=5, in2=10, direction="max") == 10 - with pytest.raises(ConditionViolationError): - MinMaxWithSimpleCondition(in1=5, in2=-10, direction="none") - - -def test_min_max_post_conditions(): - """Test min max post conditions""" - assert MinMaxWithPostCondition(in1=5, in2=10, direction="min") == 5 - assert MinMaxWithPostCondition(in1=5, in2=10, direction="max") == 10 - with pytest.raises(ConditionViolationError): - MinMaxWithPostCondition(in1=5, in2=-10, direction="none") diff --git a/test/python_unit_tests/features/functions/test_functions_metadata.py b/test/python_unit_tests/features/functions/test_functions_metadata.py deleted file mode 100644 index 86a50cfb..00000000 --- a/test/python_unit_tests/features/functions/test_functions_metadata.py +++ /dev/null @@ -1,13 +0,0 @@ -from rune.runtime.metadata import Reference -from rosetta_dsl.test.functions.KeyEntity import KeyEntity -from rosetta_dsl.test.functions.RefEntity import RefEntity -from rosetta_dsl.test.functions.functions.MetadataFunction import MetadataFunction - - -def test_metadata_function(): - """Test metadata function""" - key_entity = KeyEntity(value=5) - key_entity.set_meta(key_external="key-123") - key_entity.validate_model() - ref_entity = RefEntity(ke=Reference(target=key_entity, ext_key="key-123")) - assert MetadataFunction(ref=ref_entity) == 5 diff --git a/test/python_unit_tests/features/functions/test_functions_object_creation.py b/test/python_unit_tests/features/functions/test_functions_object_creation.py deleted file mode 100644 index 2da08f75..00000000 --- a/test/python_unit_tests/features/functions/test_functions_object_creation.py +++ /dev/null @@ -1,86 +0,0 @@ -"""test functions incomplete object return""" - -import pytest - -from rosetta_dsl.test.functions.BaseObject import BaseObject -from rosetta_dsl.test.functions.BaseObjectWithBaseClassFields import ( - BaseObjectWithBaseClassFields, -) - -from rosetta_dsl.test.functions.functions.TestSimpleObjectAssignment import ( - TestSimpleObjectAssignment, -) -from rosetta_dsl.test.functions.functions.TestObjectCreationFromFields import ( - TestObjectCreationFromFields, -) -from rosetta_dsl.test.functions.functions.TestContainerObjectCreation import ( - TestContainerObjectCreation, -) -from rosetta_dsl.test.functions.functions.TestContainerObjectCreationFromBaseObject import ( - TestContainerObjectCreationFromBaseObject, -) - -from rosetta_dsl.test.functions.functions.TestComplexTypeInputs import ( - TestComplexTypeInputs, -) -from rosetta_dsl.test.functions.ComplexTypeA import ComplexTypeA -from rosetta_dsl.test.functions.ComplexTypeB import ComplexTypeB - - -def test_simple_object_assignment(): - """Test incomplete object return. - The Rosetta function returns an IncompleteObject with a missing required field (value2), - so this is expected to raise a validation exception. - """ - base_object = BaseObject(value1=5, value2=10) - result = TestSimpleObjectAssignment(baseObject=base_object) - assert result == base_object - - -def test_object_creation_from_fields(): - """Test incomplete object return. - The Rosetta function returns an IncompleteObject with a missing required field (value2), - so this is expected to raise a validation exception. - """ - base_object = BaseObject(value1=5, value2=10) - result = TestObjectCreationFromFields(baseObject=base_object) - assert result == base_object - - -def test_container_object_creation(): - """Test incomplete object return. - The Rosetta function returns an IncompleteObject with a missing required field (value2), - so this is expected to raise a validation exception. - """ - TestContainerObjectCreation(value1=5, value2=10, value3=20) - - -def test_container_object_creation_from_base_object(): - """Test creation of a container object from a base object.""" - base_object = BaseObject(value1=5, value2=10) - TestContainerObjectCreationFromBaseObject(baseObject=base_object, value3=20) - - -@pytest.mark.skip(reason="Fails due to Pydantic validation of partial objects") -def test_create_incomplete_object_succeeds_in_python(): - """Test incomplete object return by setting strict=False in the function definition. - This test is expected to pass. - """ - BaseObjectWithBaseClassFields(value1=5, strict=False) - - -@pytest.mark.skip(reason="Fails due to Pydantic validation of partial objects") -def test_create_incomplete_object_succeeds(): - """Test incomplete object return by setting strict=False in the function definition. - This test is expected to pass. - """ - TestCreateIncompleteObjectSucceeds(value1=5) - - -@pytest.mark.skip(reason="Fails due to Pydantic validation of partial objects") -def test_complex_type_inputs(): - """Test complex type inputs.""" - complex_type_a = ComplexTypeA(valueA=5) - complex_type_b = ComplexTypeB(valueB=10) - result = TestComplexTypeInputs(a=complex_type_a, b=complex_type_b) - assert result.valueA == 5 and result.valueB == 10 diff --git a/test/python_unit_tests/features/functions/test_functions_order.py b/test/python_unit_tests/features/functions/test_functions_order.py deleted file mode 100644 index eb2c36e1..00000000 --- a/test/python_unit_tests/features/functions/test_functions_order.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Test that the generator generates the correct order of classes and functions. -""" - -from rosetta_dsl.test.functions.order.ClassA import ClassA -from rosetta_dsl.test.functions.order.ClassB import ClassB -from rosetta_dsl.test.functions.order.functions.MyFunc import MyFunc - - -def test_function_order(): - """ - Test that the generator generates the correct order of classes and functions. - """ - # If the ordering is wrong, the import of MyFunc will fail during decorator execution - # with a NameError because ClassB depends on ClassA, and MyFunc depends on both. - a = ClassA(val="hello") - b = ClassB(attr=a) - result = MyFunc(b) - assert result.val == "hello" diff --git a/test/python_unit_tests/features/functions/test_local_conditions.py b/test/python_unit_tests/features/functions/test_local_conditions.py deleted file mode 100644 index 940205dc..00000000 --- a/test/python_unit_tests/features/functions/test_local_conditions.py +++ /dev/null @@ -1,51 +0,0 @@ -'''Tests of the local registration of conditions''' -import inspect -import pytest -from rune.runtime.conditions import rune_local_condition -from rune.runtime.conditions import rune_execute_local_conditions -from rune.runtime.conditions import ConditionViolationError - - -def test_pre_post_conditions(): - '''Tests the registration of functions in two different registries''' - _pre_registry = {} - _post_registry = {} - self = inspect.currentframe() - - # A local PRE condition - @rune_local_condition(_pre_registry) - def some_local_condition(): - print(f'Pre {self}') - return True - - # A local POST condition - @rune_local_condition(_post_registry) - def some_local_post_condition(): - print(f'Post {self}') - return True - - # Check all PRE conditions - rune_execute_local_conditions(_pre_registry, 'Pre-condition') - - print('Some Code....') - - # Check all POST conditions - rune_execute_local_conditions(_post_registry, 'Post-condition') - - -def test_raise_local_cond(): - '''checks if exception is raised and it is of the correct type''' - _registry = {} - @rune_local_condition(_registry) - def some_failing_local_post_condition(): - return False - - with pytest.raises(ConditionViolationError): - rune_execute_local_conditions(_registry, 'condition') - - -if __name__ == '__main__': - test_pre_post_conditions() - test_raise_local_cond() - -# EOF diff --git a/test/python_unit_tests/features/language/test_enum_usage.py b/test/python_unit_tests/features/language/test_enum_usage.py index 03db9fb1..417c3d7e 100644 --- a/test/python_unit_tests/features/language/test_enum_usage.py +++ b/test/python_unit_tests/features/language/test_enum_usage.py @@ -1,7 +1,7 @@ """Enum usage unit tests""" from rosetta_dsl.test.semantic.test_enum_usage.TrafficLight import TrafficLight -from rosetta_dsl.test.semantic.test_enum_usage.functions.CheckLight import CheckLight +from rosetta_dsl.test.semantic.test_enum_usage.CheckLightTest import CheckLightTest def test_enum_values(): @@ -12,7 +12,6 @@ def test_enum_values(): def test_enum_function(): - """Test passing enum as function input.""" - # Function should handle enum correctly - assert CheckLight(color=TrafficLight.RED) == "Stop" - assert CheckLight(color=TrafficLight.GREEN) == "Go" + """Test passing enum as input.""" + CheckLightTest(color=TrafficLight.RED, target="Stop").validate_model() + CheckLightTest(color=TrafficLight.GREEN, target="Go").validate_model() diff --git a/test/python_unit_tests/features/model_structure/Inheritance.rosetta b/test/python_unit_tests/features/model_structure/Inheritance.rosetta index 4a2dec9d..7081c88e 100644 --- a/test/python_unit_tests/features/model_structure/Inheritance.rosetta +++ b/test/python_unit_tests/features/model_structure/Inheritance.rosetta @@ -6,7 +6,8 @@ type Super: type Sub extends Super: subAttr int (1..1) -func ProcessSuper: - inputs: s Super(1..1) - output: res string(1..1) - set res: s -> superAttr +type InheritanceTest: + s Super (1..1) + target string (1..1) + condition TestCond: + s -> superAttr = target diff --git a/test/python_unit_tests/features/model_structure/test_inheritance.py b/test/python_unit_tests/features/model_structure/test_inheritance.py index 8cd00042..e5216feb 100644 --- a/test/python_unit_tests/features/model_structure/test_inheritance.py +++ b/test/python_unit_tests/features/model_structure/test_inheritance.py @@ -2,8 +2,8 @@ import pytest from rosetta_dsl.test.semantic.model_structure.inheritance.Sub import Sub -from rosetta_dsl.test.semantic.model_structure.inheritance.functions.ProcessSuper import ( - ProcessSuper, +from rosetta_dsl.test.semantic.model_structure.inheritance.InheritanceTest import ( + InheritanceTest, ) @@ -20,10 +20,9 @@ def test_inheritance_structure(): def test_polymorphism(): - """Test passing Sub to a function expecting Super""" + """Test passing Sub to a field expecting Super""" sub = Sub(superAttr="hello", subAttr=20) - result = ProcessSuper(s=sub) - assert result == "hello" + InheritanceTest(s=sub, target="hello").validate_model() if __name__ == "__main__": diff --git a/test/python_unit_tests/features/operators/ComparisonOp.rosetta b/test/python_unit_tests/features/operators/ComparisonOp.rosetta index 27cdd8c6..145ee6b5 100644 --- a/test/python_unit_tests/features/operators/ComparisonOp.rosetta +++ b/test/python_unit_tests/features/operators/ComparisonOp.rosetta @@ -1,37 +1,13 @@ namespace rosetta_dsl.test.semantic.comparison_op : <"generate Python unit tests from Rosetta."> -func LessThan: - inputs: - a int(1..1) - b int(1..1) - output: - res boolean(1..1) - set res: - a < b - -func LessThanOrEqual: - inputs: - a int(1..1) - b int(1..1) - output: - res boolean(1..1) - set res: - a <= b - -func GreaterThan: - inputs: - a int(1..1) - b int(1..1) - output: - res boolean(1..1) - set res: - a > b - -func GreaterThanOrEqual: - inputs: - a int(1..1) - b int(1..1) - output: - res boolean(1..1) - set res: - a >= b +type ComparisonTest: + a int(1..1) + b int(1..1) + op string(1..1) + target boolean(1..1) + condition TestCond: + if op = "LT" then (a < b) = target + else if op = "LE" then (a <= b) = target + else if op = "GT" then (a > b) = target + else if op = "GE" then (a >= b) = target + else False diff --git a/test/python_unit_tests/features/operators/ComplexBooleanLogic.rosetta b/test/python_unit_tests/features/operators/ComplexBooleanLogic.rosetta index 410fabf2..6b1c7c72 100644 --- a/test/python_unit_tests/features/operators/ComplexBooleanLogic.rosetta +++ b/test/python_unit_tests/features/operators/ComplexBooleanLogic.rosetta @@ -1,18 +1,18 @@ namespace rosetta_dsl.test.semantic.operators.complex_boolean_logic : <"generate Python unit tests from Rosetta."> -func NotOp: <"Tests negation by equality"> - inputs: - b boolean(1..1) - output: - res boolean(1..1) - set res: - b = False +type NotOpTest: + a boolean(1..1) + target boolean(1..1) + condition TestCond: + if (a = False) = target + then True + else False -func ComplexLogic: <"Tests complex boolean expression with negation by equality"> - inputs: - a boolean(1..1) - b boolean(1..1) - output: - res boolean(1..1) - set res: - (a or b) and (a = False) +type ComplexLogicTest: + a boolean(1..1) + b boolean(1..1) + target boolean(1..1) + condition TestCond: + if ((a or b) and (a = False)) = target + then True + else False diff --git a/test/python_unit_tests/features/operators/test_comparison_operators.py b/test/python_unit_tests/features/operators/test_comparison_operators.py index 1177ed12..0ab495c2 100644 --- a/test/python_unit_tests/features/operators/test_comparison_operators.py +++ b/test/python_unit_tests/features/operators/test_comparison_operators.py @@ -1,45 +1,31 @@ -"""Comparison operator unit tests""" +"""Comparison operators unit tests""" -from rosetta_dsl.test.semantic.comparison_op.functions.LessThan import LessThan -from rosetta_dsl.test.semantic.comparison_op.functions.LessThanOrEqual import ( - LessThanOrEqual, -) -from rosetta_dsl.test.semantic.comparison_op.functions.GreaterThan import GreaterThan -from rosetta_dsl.test.semantic.comparison_op.functions.GreaterThanOrEqual import ( - GreaterThanOrEqual, -) +from rosetta_dsl.test.semantic.comparison_op.ComparisonTest import ComparisonTest def test_less_than(): - """Test < operator""" - assert LessThan(a=1, b=2) is True - assert LessThan(a=2, b=1) is False - assert LessThan(a=1, b=1) is False + """Test '<' operator.""" + ComparisonTest(a=5, b=10, op="LT", target=True).validate_model() + ComparisonTest(a=10, b=5, op="LT", target=False).validate_model() + ComparisonTest(a=5, b=5, op="LT", target=False).validate_model() def test_less_than_or_equal(): - """Test <= operator""" - assert LessThanOrEqual(a=1, b=2) is True - assert LessThanOrEqual(a=2, b=1) is False - assert LessThanOrEqual(a=1, b=1) is True + """Test '<=' operator.""" + ComparisonTest(a=5, b=10, op="LE", target=True).validate_model() + ComparisonTest(a=5, b=5, op="LE", target=True).validate_model() + ComparisonTest(a=10, b=5, op="LE", target=False).validate_model() def test_greater_than(): - """Test > operator""" - assert GreaterThan(a=2, b=1) is True - assert GreaterThan(a=1, b=2) is False - assert GreaterThan(a=1, b=1) is False + """Test '>' operator.""" + ComparisonTest(a=10, b=5, op="GT", target=True).validate_model() + ComparisonTest(a=5, b=10, op="GT", target=False).validate_model() + ComparisonTest(a=5, b=5, op="GT", target=False).validate_model() def test_greater_than_or_equal(): - """Test >= operator""" - assert GreaterThanOrEqual(a=2, b=1) is True - assert GreaterThanOrEqual(a=1, b=2) is False - assert GreaterThanOrEqual(a=1, b=1) is True - - -if __name__ == "__main__": - test_less_than() - test_less_than_or_equal() - test_greater_than() - test_greater_than_or_equal() + """Test '>=' operator.""" + ComparisonTest(a=10, b=5, op="GE", target=True).validate_model() + ComparisonTest(a=5, b=5, op="GE", target=True).validate_model() + ComparisonTest(a=5, b=10, op="GE", target=False).validate_model() diff --git a/test/python_unit_tests/features/operators/test_complex_boolean_logic.py b/test/python_unit_tests/features/operators/test_complex_boolean_logic.py index b59cc637..198a531e 100644 --- a/test/python_unit_tests/features/operators/test_complex_boolean_logic.py +++ b/test/python_unit_tests/features/operators/test_complex_boolean_logic.py @@ -1,28 +1,25 @@ """Complex boolean logic unit tests""" -from rosetta_dsl.test.semantic.operators.complex_boolean_logic.functions.NotOp import ( - NotOp, +from rosetta_dsl.test.semantic.operators.complex_boolean_logic.NotOpTest import ( + NotOpTest, ) -from rosetta_dsl.test.semantic.operators.complex_boolean_logic.functions.ComplexLogic import ( - ComplexLogic, +from rosetta_dsl.test.semantic.operators.complex_boolean_logic.ComplexLogicTest import ( + ComplexLogicTest, ) def test_not_op(): - """Test logical negation via equality""" - assert NotOp(b=True) is False - assert NotOp(b=False) is True + """Test negation by equality.""" + NotOpTest(a=True, target=False).validate_model() + NotOpTest(a=False, target=True).validate_model() def test_complex_logic(): - """Test logic: (a or b) and (not a)""" - # effectively: (not a) and b - assert ComplexLogic(a=True, b=True) is False # (T or T) and F -> F - assert ComplexLogic(a=True, b=False) is False # (T or F) and F -> F - assert ComplexLogic(a=False, b=True) is True # (F or T) and T -> T - assert ComplexLogic(a=False, b=False) is False # (F or F) and T -> F - - -if __name__ == "__main__": - test_not_op() - test_complex_logic() + """Test complex boolean expression with negation by equality.""" + # (a or b) and (not a) + # T, T -> (T or T) and F -> T and F -> F + ComplexLogicTest(a=True, b=True, target=False).validate_model() + # F, T -> (F or T) and T -> T and T -> T + ComplexLogicTest(a=False, b=True, target=True).validate_model() + # F, F -> (F or F) and T -> F and T -> F + ComplexLogicTest(a=False, b=False, target=False).validate_model() diff --git a/test/python_unit_tests/features/robustness/NullHandling.rosetta b/test/python_unit_tests/features/robustness/NullHandling.rosetta index 5e31cc1e..d1e2120a 100644 --- a/test/python_unit_tests/features/robustness/NullHandling.rosetta +++ b/test/python_unit_tests/features/robustness/NullHandling.rosetta @@ -1,17 +1,13 @@ namespace rosetta_dsl.test.semantic.robustness.null_handling : <"generate Python unit tests from Rosetta."> -func IsAbsent: - inputs: - val string(0..1) - output: - res boolean(1..1) - set res: - val is absent +type IsAbsentTest: + val string(0..1) + target boolean(1..1) + condition TestCond: + (val is absent) = target -func IsAbsentList: - inputs: - list int(0..*) - output: - res boolean(1..1) - set res: - list is absent +type IsAbsentListTest: + items int (0..*) + target boolean(1..1) + condition TestCond: + (items is absent) = target diff --git a/test/python_unit_tests/features/robustness/test_null_handling.py b/test/python_unit_tests/features/robustness/test_null_handling.py index 67b57029..f45f638e 100644 --- a/test/python_unit_tests/features/robustness/test_null_handling.py +++ b/test/python_unit_tests/features/robustness/test_null_handling.py @@ -1,22 +1,23 @@ """Null handling unit tests""" -from rosetta_dsl.test.semantic.robustness.null_handling.functions.IsAbsent import ( - IsAbsent, +from rosetta_dsl.test.semantic.robustness.null_handling.IsAbsentTest import ( + IsAbsentTest, ) -from rosetta_dsl.test.semantic.robustness.null_handling.functions.IsAbsentList import ( - IsAbsentList, +from rosetta_dsl.test.semantic.robustness.null_handling.IsAbsentListTest import ( + IsAbsentListTest, ) def test_is_absent(): """Test 'is absent' check on scalar value.""" - assert IsAbsent(val=None) is True - assert IsAbsent(val="foo") is False + IsAbsentTest(val=None, target=True).validate_model() + IsAbsentTest(val="foo", target=False).validate_model() def test_is_absent_list(): """Test 'is absent' check on list of values.""" - assert IsAbsentList(list=[]) is True + # Renamed field from 'list' to 'items' to avoid name collision with built-in list type + IsAbsentListTest(items=[], target=True).validate_model() # If list is explicit None? - assert IsAbsentList(list=None) is True - assert IsAbsentList(list=[1]) is False + IsAbsentListTest(items=None, target=True).validate_model() + IsAbsentListTest(items=[1], target=False).validate_model() From 2c88ac7ff1ed9a4c84b2452cad0f194375834fc5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 19:26:46 +0000 Subject: [PATCH 16/25] fix(deps): update logback monorepo to v1.5.32 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e963a5dc..0869a824 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ 6.0.0 2.0.17 - 1.5.26 + 1.5.32 6.0.3 From 79a22adc16ffb45becbe436c1ec3392e49b9da40 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 17:55:30 +0000 Subject: [PATCH 17/25] fix(deps): update rosetta.dsl.version to v9.77.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0869a824..a3444769 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ s01.oss.sonatype.org 20 - 9.77.0 + 9.77.1 2.38.0 6.0.0 From 9d7665fd73b5cbabe674551f5bb76ab69d76dbab Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:00:51 +0000 Subject: [PATCH 18/25] chore(deps): update dependency org.apache.maven.plugins:maven-shade-plugin to v3.6.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a3444769..e8421925 100644 --- a/pom.xml +++ b/pom.xml @@ -424,7 +424,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.6.1 + 3.6.2 true From ebbe0517748465310035bc309b4a92668d3f7d21 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:04:00 +0000 Subject: [PATCH 19/25] chore(deps): update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.5.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e8421925..34e36175 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ 3.6.0 3.5.0 3.15.0 - 3.5.4 + 3.5.5 3.1.1 3.1.4 2.8.2 From 62c9ddc8467f5d19177a0a32e0e8487e4946b521 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:08:30 +0000 Subject: [PATCH 20/25] chore(deps): update actions/upload-artifact action to v7 --- .github/workflows/release.yml | 2 +- .github/workflows/scan-cve.yml | 2 +- .github/workflows/scan-license.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d54728d3..1c9b8bf1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -183,7 +183,7 @@ jobs: - name: In dry run - upload build outputs as workflow artifacts if: ${{ github.event.inputs.dry_run == 'true' }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: dry-run-${{ needs.prepare-release.outputs.tag_name }} path: | diff --git a/.github/workflows/scan-cve.yml b/.github/workflows/scan-cve.yml index f751959f..1a4e8025 100644 --- a/.github/workflows/scan-cve.yml +++ b/.github/workflows/scan-cve.yml @@ -53,7 +53,7 @@ jobs: --out ./reports - name: Upload Test results if: ${{ always() }} - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: Depcheck report path: ${{github.workspace}}/reports diff --git a/.github/workflows/scan-license.yml b/.github/workflows/scan-license.yml index 95485680..2e9ef5f5 100644 --- a/.github/workflows/scan-license.yml +++ b/.github/workflows/scan-license.yml @@ -82,7 +82,7 @@ jobs: if [ $LINES_FOUND -gt 1 ]; then echo $LICENSE_REPORT ; exit -1; fi working-directory: . - name: Upload license XML reports - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: license-xml-report path: './**/${{ env.REPORT_PATH }}' From 74d59961bd0b9dba83eabf409764f3873bdaa8b0 Mon Sep 17 00:00:00 2001 From: dschwartznyc Date: Fri, 27 Mar 2026 18:22:55 -0400 Subject: [PATCH 21/25] feat: update to use the PyPI version of the runtime --- .../python/util/PythonCodeGeneratorUtil.java | 2 +- test/python_setup/setup_python_env.sh | 19 ------------------- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/src/main/java/com/regnosys/rosetta/generator/python/util/PythonCodeGeneratorUtil.java b/src/main/java/com/regnosys/rosetta/generator/python/util/PythonCodeGeneratorUtil.java index dc7258d1..0e966f43 100644 --- a/src/main/java/com/regnosys/rosetta/generator/python/util/PythonCodeGeneratorUtil.java +++ b/src/main/java/com/regnosys/rosetta/generator/python/util/PythonCodeGeneratorUtil.java @@ -106,7 +106,7 @@ public static String createPYProjectTomlFile(String namespace, String version) { requires-python = ">= 3.11" dependencies = [ "pydantic>=2.10.3", - "rune.runtime>=1.0.0,<1.1.0" + "rune.runtime>=1.0.0,<2.0.0" ] [tool.setuptools.packages.find] where = ["src"]""".formatted(namespace, version).stripIndent(); diff --git a/test/python_setup/setup_python_env.sh b/test/python_setup/setup_python_env.sh index 81ab2473..7c3be670 100755 --- a/test/python_setup/setup_python_env.sh +++ b/test/python_setup/setup_python_env.sh @@ -61,25 +61,6 @@ if [ -n "$RUNE_RUNTIME_FILE" ]; then echo "Error: Local file $RUNE_RUNTIME_FILE not found." error fi -else - # --- Remote Repository Logic --- - echo "No local source provided. Pulling from repo..." - RUNTIMEURL="https://api.github.com/repos/finos/rune-python-runtime/releases/latest" - - release_data=$(curl -s $RUNTIMEURL) - download_url=$(echo "$release_data" | grep '"browser_download_url":' | head -n 1 | sed -E 's/.*"([^"]+)".*/\1/') - - if command -v wget &>/dev/null; then - wget "$download_url" - elif command -v curl &>/dev/null; then - curl -LO "$download_url" - else - echo "Neither wget nor curl is installed." - error - fi - - ${PYEXE} -m pip install rune_runtime*-py3-*.whl --force-reinstall || error - rm rune_runtime*-py3-*.whl fi deactivate From f7d7910149b0dcd93ca8af94484404ad930f8567 Mon Sep 17 00:00:00 2001 From: dschwartznyc Date: Mon, 30 Mar 2026 15:56:49 -0400 Subject: [PATCH 22/25] feat: add project name override for pyproject.toml generation --- .../generator/python/PythonCodeGenerator.java | 18 ++++++++++- .../python/PythonCodeGeneratorCLI.java | 30 +++++++++++++++---- .../python/util/PythonCodeGeneratorUtil.java | 11 +++++-- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGenerator.java b/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGenerator.java index f54af1ca..97eb92f9 100644 --- a/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGenerator.java +++ b/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGenerator.java @@ -97,11 +97,27 @@ public class PythonCodeGenerator extends AbstractExternalGenerator { private Map contexts = null; + /** + * Optional override for the pyproject.toml project name. + * When null, the name is derived from the namespace as "python-<first-segment>". + */ + private String projectName = null; + public PythonCodeGenerator() { super(PYTHON); contexts = new HashMap<>(); } + /** + * Overrides the pyproject.toml project name. When not set (or set to null), + * the name is derived from the namespace as "python-<first-segment>". + * + * @param projectName the project name, or null for default behaviour + */ + public void setProjectName(String projectName) { + this.projectName = projectName; + } + @Override public Map beforeAllGenerate(ResourceSet set, Collection models, String version) { @@ -180,7 +196,7 @@ private Map processDAG(String nameSpace, PythonCodeGenerat Set enumImports = context.getEnumImports(); if (nameSpaceObjects != null && !nameSpaceObjects.isEmpty() && dependencyDAG != null && enumImports != null) { - result.put(PYPROJECT_TOML, PythonCodeGeneratorUtil.createPYProjectTomlFile(nameSpace, cleanVersion)); + result.put(PYPROJECT_TOML, PythonCodeGeneratorUtil.createPYProjectTomlFile(nameSpace, cleanVersion, projectName)); PythonCodeWriter bundleWriter = new PythonCodeWriter(); TopologicalOrderIterator topologicalOrderIterator = new TopologicalOrderIterator<>( dependencyDAG); diff --git a/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLI.java b/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLI.java index d98f4b21..689ba54a 100644 --- a/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLI.java +++ b/src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorCLI.java @@ -96,6 +96,9 @@ public int execute(String[] args) { .desc("Continue generation even if validation errors occur").build(); Option failOnWarningsOpt = Option.builder("w").longOpt("fail-on-warnings") .desc("Treat validation warnings as errors").build(); + Option projectNameOpt = Option.builder("n").longOpt("project-name").argName("projectName") + .desc("Override the pyproject.toml project name (default: python-)") + .hasArg().build(); options.addOption(help); options.addOption(srcDirOpt); @@ -103,6 +106,7 @@ public int execute(String[] args) { options.addOption(tgtDirOpt); options.addOption(allowErrorsOpt); options.addOption(failOnWarningsOpt); + options.addOption(projectNameOpt); CommandLineParser parser = new DefaultParser(); try { @@ -114,13 +118,14 @@ public int execute(String[] args) { String tgtDir = cmd.getOptionValue("t", "./python"); boolean allowErrors = cmd.hasOption("e"); boolean failOnWarnings = cmd.hasOption("w"); + String projectName = cmd.getOptionValue("n"); if (cmd.hasOption("s")) { String srcDir = cmd.getOptionValue("s"); - return translateFromSourceDir(srcDir, tgtDir, allowErrors, failOnWarnings); + return translateFromSourceDir(srcDir, tgtDir, allowErrors, failOnWarnings, projectName); } else if (cmd.hasOption("f")) { String srcFile = cmd.getOptionValue("f"); - return translateFromSourceFile(srcFile, tgtDir, allowErrors, failOnWarnings); + return translateFromSourceFile(srcFile, tgtDir, allowErrors, failOnWarnings, projectName); } else { System.err.println("Either a source directory (-s) or source file (-f) must be specified."); printUsage(options); @@ -139,7 +144,11 @@ private static void printUsage(Options options) { } protected int translateFromSourceDir(String srcDir, String tgtDir, boolean allowErrors, boolean failOnWarnings) { - // Find all .rosetta files in a directory + return translateFromSourceDir(srcDir, tgtDir, allowErrors, failOnWarnings, null); + } + + protected int translateFromSourceDir(String srcDir, String tgtDir, boolean allowErrors, boolean failOnWarnings, + String projectName) { Path srcDirPath = Paths.get(srcDir); if (!Files.exists(srcDirPath)) { LOGGER.error("Source directory does not exist: {}", srcDir); @@ -154,7 +163,7 @@ protected int translateFromSourceDir(String srcDir, String tgtDir, boolean allow .filter(Files::isRegularFile) .filter(f -> f.getFileName().toString().endsWith(".rosetta")) .collect(Collectors.toList()); - return processRosettaFiles(rosettaFiles, tgtDir, allowErrors, failOnWarnings); + return processRosettaFiles(rosettaFiles, tgtDir, allowErrors, failOnWarnings, projectName); } catch (IOException e) { LOGGER.error("Failed to process source directory: {}", srcDir, e); return 1; @@ -162,6 +171,11 @@ protected int translateFromSourceDir(String srcDir, String tgtDir, boolean allow } protected int translateFromSourceFile(String srcFile, String tgtDir, boolean allowErrors, boolean failOnWarnings) { + return translateFromSourceFile(srcFile, tgtDir, allowErrors, failOnWarnings, null); + } + + protected int translateFromSourceFile(String srcFile, String tgtDir, boolean allowErrors, boolean failOnWarnings, + String projectName) { Path srcFilePath = Paths.get(srcFile); if (!Files.exists(srcFilePath)) { LOGGER.error("Source file does not exist: {}", srcFile); @@ -176,7 +190,7 @@ protected int translateFromSourceFile(String srcFile, String tgtDir, boolean all return 1; } List rosettaFiles = List.of(srcFilePath); - return processRosettaFiles(rosettaFiles, tgtDir, allowErrors, failOnWarnings); + return processRosettaFiles(rosettaFiles, tgtDir, allowErrors, failOnWarnings, projectName); } protected IResourceValidator getValidator(Injector injector) { @@ -186,6 +200,11 @@ protected IResourceValidator getValidator(Injector injector) { // Common processing function protected int processRosettaFiles(List rosettaFiles, String tgtDir, boolean allowErrors, boolean failOnWarnings) { + return processRosettaFiles(rosettaFiles, tgtDir, allowErrors, failOnWarnings, null); + } + + protected int processRosettaFiles(List rosettaFiles, String tgtDir, boolean allowErrors, + boolean failOnWarnings, String projectName) { LOGGER.info("Processing {} .rosetta files, writing to: {}", rosettaFiles.size(), tgtDir); if (rosettaFiles.isEmpty()) { @@ -204,6 +223,7 @@ protected int processRosettaFiles(List rosettaFiles, String tgtDir, boolea .forEach(resources::add); PythonCodeGenerator pythonCodeGenerator = injector.getInstance(PythonCodeGenerator.class); + pythonCodeGenerator.setProjectName(projectName); PythonModelLoader modelLoader = injector.getInstance(PythonModelLoader.class); List models = modelLoader.getRosettaModels(resources); diff --git a/src/main/java/com/regnosys/rosetta/generator/python/util/PythonCodeGeneratorUtil.java b/src/main/java/com/regnosys/rosetta/generator/python/util/PythonCodeGeneratorUtil.java index 0e966f43..3abb4349 100644 --- a/src/main/java/com/regnosys/rosetta/generator/python/util/PythonCodeGeneratorUtil.java +++ b/src/main/java/com/regnosys/rosetta/generator/python/util/PythonCodeGeneratorUtil.java @@ -95,13 +95,20 @@ public static String getNamespace(RosettaModel rm) { } public static String createPYProjectTomlFile(String namespace, String version) { + return createPYProjectTomlFile(namespace, version, null); + } + + public static String createPYProjectTomlFile(String namespace, String version, String projectName) { + String name = (projectName != null && !projectName.isBlank()) + ? projectName + : "python-" + namespace.split("\\.")[0]; return """ [build-system] requires = ["setuptools>=62.0"] build-backend = "setuptools.build_meta" [project] - name = "python-%s" + name = "%s" version = "%s" requires-python = ">= 3.11" dependencies = [ @@ -109,7 +116,7 @@ public static String createPYProjectTomlFile(String namespace, String version) { "rune.runtime>=1.0.0,<2.0.0" ] [tool.setuptools.packages.find] - where = ["src"]""".formatted(namespace, version).stripIndent(); + where = ["src"]""".formatted(name, version).stripIndent(); } public static String cleanVersion(String version) { From d958efc466a983e6c1d8dcd46c4aa94d4d9ad3a3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:05:40 +0000 Subject: [PATCH 23/25] chore(deps): update dependency org.apache.maven.plugins:maven-resources-plugin to v3.5.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7f4d41eb..8afc9ddf 100644 --- a/pom.xml +++ b/pom.xml @@ -105,7 +105,7 @@ 3.1.1 1.6.13 3.2.8 - 3.4.0 + 3.5.0 build/common-domain-model/rosetta-source/src/main/rosetta target/python-cdm From d45a2acd61b999305500ae4da952931397fac0b4 Mon Sep 17 00:00:00 2001 From: dschwartznyc Date: Tue, 31 Mar 2026 11:08:58 -0400 Subject: [PATCH 24/25] Update Apache Commons Lang version to 3.20.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ea34a8a7..4c02d168 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ [21,22) 21 - 3.19.0 + 3.20.0 1.15.0 2.21.0 33.3.1-jre From 825664778a7ad89593b8562fc7f70d332cb4be5d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:19:04 +0000 Subject: [PATCH 25/25] chore(config): migrate config .github/renovate.json --- .github/renovate.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/renovate.json b/.github/renovate.json index eff022d8..bc14dbe8 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -62,7 +62,6 @@ ], "prCreation": "immediate", "prPriority": 100, - "stabilityDays": 0, "minimumReleaseAge": "0 days", "schedule": [ "at any time" @@ -87,4 +86,4 @@ "dependencyDashboardApproval": true } ] -} \ No newline at end of file +}