Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions python-test/unit-tests/features/expressions/Constructor.rosetta
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace rosetta_dsl.test.expressions.constructor

type Foo:
a int (1..1)
b int (0..1)

type Item:
val int (1..1)

type Container:
items Item (0..*)

func BuildFooComplete:
inputs: seed int (1..1)
output: result Foo (1..1)
set result:
Foo { a: seed, b: 2 }

func BuildFooPartial:
inputs: val int (1..1)
output: result Foo (1..1)
set result:
Foo { a: val, ... }

func BuildContainer:
inputs: n int (1..1)
output: result Container (1..1)
set result:
Container {
items: [ Item { val: 1 }, Item { val: 2 }, Item { val: n } ]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace rosetta_dsl.test.expressions.empty_evaluation

type Foo:
someBoolean boolean (0..1)
alwaysFalse boolean (1..1)

func IsAbsent:
inputs: foo Foo (1..1)
output: result boolean (1..1)
set result:
foo -> someBoolean is absent

func IsPresent:
inputs: foo Foo (1..1)
output: result boolean (1..1)
set result:
foo -> someBoolean exists
12 changes: 12 additions & 0 deletions python-test/unit-tests/features/expressions/SortClosure.rosetta
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,15 @@ func SortItems:
output: result SortItem (0..*)
set result:
items sort [ item -> val1 ]

func SortItemsBySecondField:
inputs: items SortItem (0..*)
output: result SortItem (0..*)
set result:
items sort [ item -> val2 ]

func SortIntegers:
inputs: items int (0..*)
output: result int (0..*)
set result:
items sort
37 changes: 37 additions & 0 deletions python-test/unit-tests/features/expressions/test_constructor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#
# Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
# SPDX-License-Identifier: Apache-2.0
#

"""Constructor expression runtime tests."""

from rosetta_dsl.test.expressions.constructor.Foo import Foo
from rosetta_dsl.test.expressions.constructor.Item import Item
from rosetta_dsl.test.expressions.constructor.Container import Container
from rosetta_dsl.test.expressions.constructor.functions.BuildFooComplete import BuildFooComplete
from rosetta_dsl.test.expressions.constructor.functions.BuildFooPartial import BuildFooPartial
from rosetta_dsl.test.expressions.constructor.functions.BuildContainer import BuildContainer


def test_constructor_with_all_fields():
"""Foo { a: seed, b: 2 } populates both fields."""
result = BuildFooComplete(seed=1)
assert result.a == 1
assert result.b == 2


def test_constructor_with_ellipsis_omits_optional_field():
"""Foo { a: val, ... } leaves the optional field b as None."""
result = BuildFooPartial(val=42)
assert result.a == 42
assert result.b is None


def test_constructor_with_list_literal():
"""Container { items: [...] } produces a list of the specified items."""
result = BuildContainer(n=3)
assert isinstance(result, Container)
assert len(result.items) == 3
assert result.items[0].val == 1
assert result.items[1].val == 2
assert result.items[2].val == 3
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#
# Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
# SPDX-License-Identifier: Apache-2.0
#

"""Empty / absent value evaluation runtime tests.

Mirrors EmptyEvaluationTest in the rune-dsl Java generator tests, adapted for
the Python generator's runtime: optional attributes default to None, is-absent
returns True when the attribute is None, and exists returns True when it is set.
"""

from rosetta_dsl.test.expressions.empty_evaluation.Foo import Foo
from rosetta_dsl.test.expressions.empty_evaluation.functions.IsAbsent import IsAbsent
from rosetta_dsl.test.expressions.empty_evaluation.functions.IsPresent import IsPresent


def test_is_absent_when_none():
"""is absent returns True when the optional attribute is None."""
foo = Foo(someBoolean=None, alwaysFalse=False)
assert IsAbsent(foo=foo) is True


def test_is_absent_when_set():
"""is absent returns False when the optional attribute has a value."""
foo = Foo(someBoolean=True, alwaysFalse=False)
assert IsAbsent(foo=foo) is False


def test_exists_when_set():
"""exists returns True when the optional attribute is set."""
foo = Foo(someBoolean=True, alwaysFalse=False)
assert IsPresent(foo=foo) is True


def test_exists_when_none():
"""exists returns False when the optional attribute is None."""
foo = Foo(someBoolean=None, alwaysFalse=False)
assert IsPresent(foo=foo) is False
33 changes: 30 additions & 3 deletions python-test/unit-tests/features/expressions/test_sort_closure.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,42 @@
# SPDX-License-Identifier: Apache-2.0
#

"""Sort Closure expression unit tests"""
"""Sort closure expression unit tests."""

from rosetta_dsl.test.expressions.sort_closure.SortItem import SortItem
from rosetta_dsl.test.expressions.sort_closure.functions.SortItems import SortItems
from rosetta_dsl.test.expressions.sort_closure.functions.SortItemsBySecondField import (
SortItemsBySecondField,
)
from rosetta_dsl.test.expressions.sort_closure.functions.SortIntegers import SortIntegers


def test_sort_closure():
"""Test sort closure expression."""
"""Sort complex objects by key field val1."""
items = [SortItem(val1=5, val2=10), SortItem(val1=1, val2=100)]

expected = [SortItem(val1=1, val2=100), SortItem(val1=5, val2=10)]
assert SortItems(items=items) == expected


def test_sort_by_second_field():
"""Sort complex objects by key field val2."""
items = [SortItem(val1=5, val2=100), SortItem(val1=1, val2=10)]
result = SortItemsBySecondField(items=items)
expected = [SortItem(val1=1, val2=10), SortItem(val1=5, val2=100)]
assert result == expected


def test_sort_integers_no_key():
"""Plain sort without a key expression sorts a list of integers."""
items = [3, 1, 4, 1, 5, 9, 2]
result = SortIntegers(items=items)
assert result == [1, 1, 2, 3, 4, 5, 9]


def test_sort_preserves_all_non_null_items():
"""All non-null items survive the sort; order is ascending by key."""
items = [SortItem(val1=10, val2=1), SortItem(val1=2, val2=5)]
result = SortItems(items=items)
assert len(result) == 2
assert result[0].val1 == 2
assert result[1].val1 == 10
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace rosetta_dsl.test.language.type_alias_condition

typeAlias StringCode:
string

condition ValidCode:
item <> "INVALID"

type CodeHolder:
code StringCode (1..1)

func BuildCodeHolder:
inputs: code string (1..1)
output: result CodeHolder (1..1)
set result:
CodeHolder { code: code }
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#
# Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
# SPDX-License-Identifier: Apache-2.0
#

"""Type alias condition tests.

Known gap: the Python generator strips type aliases to their underlying
primitive types (e.g. StringCode -> str). Conditions defined on the
type alias (e.g. ValidCode: item <> "INVALID") are NOT enforced at
runtime in Python.

The Java generator, by contrast, generates a named validator (e.g.
StringCodeValidCode) that fires whenever an attribute of that alias
type is validated.

These tests document current behaviour: the alias is transparent and
the underlying primitive is used without condition enforcement.

Mirrors TypeAliasConditionTest in the rune-dsl Java generator tests.
"""

from rosetta_dsl.test.language.type_alias_condition.CodeHolder import CodeHolder
from rosetta_dsl.test.language.type_alias_condition.functions.BuildCodeHolder import (
BuildCodeHolder,
)


def test_type_alias_reduces_to_underlying_type():
"""CodeHolder.code is a plain str field; the alias name is transparent."""
holder = BuildCodeHolder(code="VALID")
assert holder.code == "VALID"
assert isinstance(holder.code, str)


def test_type_alias_condition_is_not_enforced():
"""
Known gap: the ValidCode condition (item <> 'INVALID') is not enforced
in Python. A value that violates the alias condition is accepted without
raising an error.
"""
# This would fail validation in the Java generator but is silently accepted here.
holder = BuildCodeHolder(code="INVALID")
assert holder.code == "INVALID" # condition NOT enforced
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,29 @@ def _else_fn0():
// From PythonReduceOperationTest
// -------------------------------------------------------------------------

// -------------------------------------------------------------------------
// From RosettaSortOperationTest — sort with key expression
// -------------------------------------------------------------------------

/**
* {@code items sort [ item -> val ]} generates a sort with a {@code key=} lambda
* that uses {@code rune_resolve_attr} to extract the key field.
*/
@Test
public void testSortWithKeyExpression() {
testUtils.assertBundleContainsExpectedString("""
type SortItem:
val int (1..1)

func SortByVal:
inputs: items SortItem (0..*)
output: result SortItem (0..*)
set result:
items sort [ item -> val ]
""",
"(lambda items: sorted((x for x in (items or []) if x is not None), key=lambda item: rune_resolve_attr(item, \"val\")) if items is not None else None)(rune_resolve_attr(self, \"items\"))");
}

@Test
public void testReduceOperation() {
Map<String, CharSequence> gf = testUtils.generatePythonFromString(
Expand Down
Loading