From 855a9ed32f22e947670dc7f5b90190da6a1527dc Mon Sep 17 00:00:00 2001 From: David Leong <116610336+leongdl@users.noreply.github.com> Date: Tue, 8 Sep 2026 14:48:12 -0700 Subject: [PATCH] test: Add FLOAT floatstring numeric-comparison conformance fixtures Closes the two coverage gaps jericht raised on #179: no job-level FLOAT analog of the INT intstring bounds fixtures, and no string-form numeric comparison for amounts min/max. Section 2.4 declares minValue, maxValue and allowedValues as ` | ` with no extension gate, and the section 2 preamble makes them constraints validation must enforce. The suite had template-level accept coverage only: 2.4--float-param-minmax-floatstring and 2.4--float-param-allowedvalues-floatstring never submit a value, and 1.1--float-parameter-string-coercion-valid declares no bounds. Nothing decided a string-form FLOAT constraint against a submitted value. Four job-level fixtures close that, paired accept and reject on both bounds and allowedValues. Bounds are '010.0' and '100.0' so the compare diverges lexically from numerically in both directions: 50.5 satisfies '100.0' numerically but sorts above it lexically, and 5.5 violates '010.0' numerically but sorts above it lexically. allowedValues holds '04.5' so membership for a submitted 4.5 can only be decided on the parsed value. Section 3.3.1 admits `` and `` for amounts min and max under FEATURE_BUNDLE_1, so the fifth fixture pins min '010.0' > max '5.0'. The existing 3.3.1--amount-min-greater-than-max.invalid.yaml uses numeric literals only, and '010.0' sorts below '5.0' lexically. Verified against openjd-cli 0.7.7 with openjd-model 0.11.9 and against openjd-rs at main 361b2c5. Slice results on both: base 656 -> 660, FEATURE_BUNDLE_1 55 -> 56, 0 failed before and after. Mutation-checked twice over. Ten fixture-level mutants, ten caught: each accept fixture with its expected output corrupted, each reject fixture with its input made legal. Five implementation-level mutants against openjd-rs, five caught: min bound unenforced, min compared lexically, allowedValues unenforced, membership rejecting a numerically-equal member, and amounts min<=max unenforced. The lexical mutant is the one that matters, and only the new negative catches it. Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com> --- ...atstring-min-greater-than-max.invalid.yaml | 27 ++++++++++++++++ ...floatstring-allowedvalues-member.test.yaml | 32 +++++++++++++++++++ ...-allowedvalues-violation.invalid.test.yaml | 29 +++++++++++++++++ ...oat-floatstring-bounds-satisfied.test.yaml | 32 +++++++++++++++++++ ...tstring-bounds-violation.invalid.test.yaml | 30 +++++++++++++++++ 5 files changed, 150 insertions(+) create mode 100644 conformance-tests/2023-09/FEATURE_BUNDLE_1/job_templates/3.3.1--amount-floatstring-min-greater-than-max.invalid.yaml create mode 100644 conformance-tests/2023-09/base/jobs/1.1--float-floatstring-allowedvalues-member.test.yaml create mode 100644 conformance-tests/2023-09/base/jobs/1.1--float-floatstring-allowedvalues-violation.invalid.test.yaml create mode 100644 conformance-tests/2023-09/base/jobs/1.1--float-floatstring-bounds-satisfied.test.yaml create mode 100644 conformance-tests/2023-09/base/jobs/1.1--float-floatstring-bounds-violation.invalid.test.yaml diff --git a/conformance-tests/2023-09/FEATURE_BUNDLE_1/job_templates/3.3.1--amount-floatstring-min-greater-than-max.invalid.yaml b/conformance-tests/2023-09/FEATURE_BUNDLE_1/job_templates/3.3.1--amount-floatstring-min-greater-than-max.invalid.yaml new file mode 100644 index 00000000..4a7518c4 --- /dev/null +++ b/conformance-tests/2023-09/FEATURE_BUNDLE_1/job_templates/3.3.1--amount-floatstring-min-greater-than-max.invalid.yaml @@ -0,0 +1,27 @@ +# Section 3.3.1 admits the string forms `` for amounts[].min and +# `` for .max under FEATURE_BUNDLE_1, so the min <= max check that +# base/job_templates/3.3.1--amount-min-greater-than-max.invalid.yaml pins for numeric literals +# has to run on the parsed values here. +# +# min is '010.0' and max is '5.0', the only defect. +# +# The template must be rejected. Lexically '010.0' sorts below '5.0', so a lexical comparison +# reads min as under max and accepts it. +specificationVersion: jobtemplate-2023-09 +extensions: +- FEATURE_BUNDLE_1 +name: TestJob +steps: +- name: Step1 + hostRequirements: + amounts: + - name: amount.worker.vcpu + min: '010.0' + max: '5.0' + script: + actions: + onRun: + command: python + args: + - "-c" + - "print()" diff --git a/conformance-tests/2023-09/base/jobs/1.1--float-floatstring-allowedvalues-member.test.yaml b/conformance-tests/2023-09/base/jobs/1.1--float-floatstring-allowedvalues-member.test.yaml new file mode 100644 index 00000000..d57e5bf5 --- /dev/null +++ b/conformance-tests/2023-09/base/jobs/1.1--float-floatstring-allowedvalues-member.test.yaml @@ -0,0 +1,32 @@ +# Section 2.4 declares allowedValues as `[ | , ... ]`, so membership is +# decided on the parsed value, not on the string form. +# +# The list is ['1.5', '2.5', '04.5'] and the submitted value is the number 4.5, which equals +# '04.5' numerically and matches no element textually. Negative twin: +# 1.1--float-floatstring-allowedvalues-violation.invalid.test.yaml. +# +# The job must be created and print 4.5. An implementation comparing string forms finds no +# match and wrongly rejects it. +template: + specificationVersion: jobtemplate-2023-09 + name: TestJob + parameterDefinitions: + - name: Scale + type: FLOAT + allowedValues: ['1.5', '2.5', '04.5'] + steps: + - name: Step1 + script: + actions: + onRun: + command: python + args: + - -c + - print(r'SCALE:{{Param.Scale}}:END') +parameters: + Scale: 4.5 +expected: + output: + - 'SCALE:4.5:END' + forbidden: + - '{{Param.' diff --git a/conformance-tests/2023-09/base/jobs/1.1--float-floatstring-allowedvalues-violation.invalid.test.yaml b/conformance-tests/2023-09/base/jobs/1.1--float-floatstring-allowedvalues-violation.invalid.test.yaml new file mode 100644 index 00000000..abf5ffb2 --- /dev/null +++ b/conformance-tests/2023-09/base/jobs/1.1--float-floatstring-allowedvalues-violation.invalid.test.yaml @@ -0,0 +1,29 @@ +# Section 2.4 declares allowedValues as `[ | , ... ]` and a value absent +# from the list is an error. +# +# The list is ['1.5', '2.5', '04.5'] and the submitted value is the number 3.5, absent under +# every reading. Positive twin: 1.1--float-floatstring-allowedvalues-member.test.yaml. +# +# Job creation must fail. An implementation that skips a string-form allowedValues list because +# it cannot parse the elements admits the job. +# +# .invalid.test.yaml, not .invalid.yaml: the template is valid on its own and the error only +# fires once the submitted value is bound. +template: + specificationVersion: jobtemplate-2023-09 + name: TestJob + parameterDefinitions: + - name: Scale + type: FLOAT + allowedValues: ['1.5', '2.5', '04.5'] + steps: + - name: Step1 + script: + actions: + onRun: + command: python + args: + - -c + - print(r'SCALE:{{Param.Scale}}:END') +parameters: + Scale: 3.5 diff --git a/conformance-tests/2023-09/base/jobs/1.1--float-floatstring-bounds-satisfied.test.yaml b/conformance-tests/2023-09/base/jobs/1.1--float-floatstring-bounds-satisfied.test.yaml new file mode 100644 index 00000000..83846b93 --- /dev/null +++ b/conformance-tests/2023-09/base/jobs/1.1--float-floatstring-bounds-satisfied.test.yaml @@ -0,0 +1,32 @@ +# Section 2.4 declares minValue and maxValue as ` | `, ungated in base +# 2023-09, and the section 2 preamble (L151) makes both constraints validation must enforce. +# +# The bounds are the strings '010.0' and '100.0' and the submitted value is the number 50.5. +# Negative twin: 1.1--float-floatstring-bounds-violation.invalid.test.yaml. +# +# The job must be created and print 50.5. A lexical comparison orders '50.5' above '100.0' and +# rejects the job instead, which is the failure this pins. +template: + specificationVersion: jobtemplate-2023-09 + name: TestJob + parameterDefinitions: + - name: Scale + type: FLOAT + minValue: '010.0' + maxValue: '100.0' + steps: + - name: Step1 + script: + actions: + onRun: + command: python + args: + - -c + - print(r'SCALE:{{Param.Scale}}:END') +parameters: + Scale: 50.5 +expected: + output: + - 'SCALE:50.5:END' + forbidden: + - '{{Param.' diff --git a/conformance-tests/2023-09/base/jobs/1.1--float-floatstring-bounds-violation.invalid.test.yaml b/conformance-tests/2023-09/base/jobs/1.1--float-floatstring-bounds-violation.invalid.test.yaml new file mode 100644 index 00000000..6d78d294 --- /dev/null +++ b/conformance-tests/2023-09/base/jobs/1.1--float-floatstring-bounds-violation.invalid.test.yaml @@ -0,0 +1,30 @@ +# Section 2.4 declares minValue as ` | `, so a string bound has to be +# parsed and compared numerically. +# +# The bounds are the strings '010.0' and '100.0' and the submitted value is the number 5.5, +# below the minimum. Positive twin: 1.1--float-floatstring-bounds-satisfied.test.yaml. +# +# Job creation must fail. Lexically '5.5' sorts above '010.0', so a lexical comparison admits +# the job, as does an implementation that cannot parse a string bound at all. +# +# .invalid.test.yaml, not .invalid.yaml: the template is valid on its own and the error only +# fires once the submitted value is bound. +template: + specificationVersion: jobtemplate-2023-09 + name: TestJob + parameterDefinitions: + - name: Scale + type: FLOAT + minValue: '010.0' + maxValue: '100.0' + steps: + - name: Step1 + script: + actions: + onRun: + command: python + args: + - -c + - print(r'SCALE:{{Param.Scale}}:END') +parameters: + Scale: 5.5