Skip to content

Commit

Permalink
Merge pull request #1559 from SachinAkash01/constraint_s
Browse files Browse the repository at this point in the history
Add negative tests for constraint mapper
  • Loading branch information
ayeshLK authored Oct 19, 2023
2 parents b4c4c6a + de2be12 commit adbdc1e
Show file tree
Hide file tree
Showing 11 changed files with 429 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public class Constants {
public static final String MEDIA_TYPE = "mediaType";
public static final String TUPLE = "tuple";
public static final String REGEX_INTERPOLATION_PATTERN = "^(?!.*\\$\\{).+$";
public static final String DATE_CONSTRAINT_ANNOTATION = "constraint:Date";

/**
* Enum to select the Ballerina Type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ public enum DiagnosticMessages {
OAS_CONVERTOR_118("OAS_CONVERTOR_118", "Generated OpenAPI definition does not contain variable " +
"assignment '%s' in constraint validation.", DiagnosticSeverity.WARNING),
OAS_CONVERTOR_119("OAS_CONVERTOR_119", "Given REGEX pattern '%s' is not supported by the OpenAPI " +
"tool, it may also not support interpolation within the REGEX pattern.", DiagnosticSeverity.WARNING);
"tool, it may also not support interpolation within the REGEX pattern.", DiagnosticSeverity.WARNING),
OAS_CONVERTOR_120("OAS_CONVERTER_120", "Ballerina Date constraints might not be reflected in the " +
"OpenAPI definition", DiagnosticSeverity.WARNING);

private final String code;
private final String description;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
import static io.ballerina.openapi.converter.Constants.HTTP;
import static io.ballerina.openapi.converter.Constants.HTTP_CODES;
import static io.ballerina.openapi.converter.Constants.REGEX_INTERPOLATION_PATTERN;
import static io.ballerina.openapi.converter.Constants.DATE_CONSTRAINT_ANNOTATION;

/**
* This util class for processing the mapping in between ballerina record and openAPI object schema.
Expand Down Expand Up @@ -832,7 +833,6 @@ private void setConstraintValueToSchema(ConstraintAnnotation constraintAnnot, Sc
}
}


/**
* This util is used to extract the annotation values in `@constraint` and store it in builder.
*/
Expand All @@ -843,6 +843,13 @@ private void extractedConstraintAnnotation(MetadataNode metadata,
.filter(this::isConstraintAnnotation)
.filter(annotation -> annotation.annotValue().isPresent())
.forEach(annotation -> {
if (isDateConstraint(annotation)) {
DiagnosticMessages errorMsg = DiagnosticMessages.OAS_CONVERTOR_120;
IncompatibleResourceDiagnostic error = new IncompatibleResourceDiagnostic(errorMsg,
annotation.location(), annotation.toString());
diagnostics.add(error);
return;
}
MappingConstructorExpressionNode annotationValue = annotation.annotValue().get();
annotationValue.fields().stream()
.filter(field -> SyntaxKind.SPECIFIC_FIELD.equals(field.kind()))
Expand All @@ -863,6 +870,16 @@ private boolean isConstraintAnnotation(AnnotationNode annotation) {
return false;

Check warning on line 870 in openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIComponentMapper.java

View check run for this annotation

Codecov / codecov/patch

openapi-bal-service/src/main/java/io/ballerina/openapi/converter/service/OpenAPIComponentMapper.java#L870

Added line #L870 was not covered by tests
}

/*
* This util is used to check whether an annotation is a constraint:Date annotation.
* Currently, we don't have support for mapping Date constraints to OAS hence we skip them.
* {@link <a href="https://github.com/ballerina-platform/ballerina-standard-library/issues/5049">...</a>}
* Once the above improvement is completed this method should be removed!
*/
private boolean isDateConstraint(AnnotationNode annotation) {
return annotation.annotReference().toString().trim().equals(DATE_CONSTRAINT_ANNOTATION);
}

/**
* This util is used to process the content of a constraint annotation.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@

import io.ballerina.openapi.cmd.OASContractGenerator;
import io.ballerina.openapi.converter.diagnostic.OpenAPIConverterDiagnostic;
import io.ballerina.projects.DiagnosticResult;
import io.ballerina.projects.Project;
import io.ballerina.projects.ProjectException;
import io.ballerina.projects.directory.ProjectLoader;
import io.ballerina.tools.diagnostics.DiagnosticSeverity;
import org.testng.Assert;
import org.testng.annotations.Test;

Expand All @@ -28,6 +33,8 @@
import java.util.Arrays;
import java.util.List;

import static io.ballerina.openapi.generators.openapi.TestUtils.getCompilation;

/**
* This test class for the covering the negative tests for constraint
* {@link io.ballerina.openapi.converter.service.ConstraintAnnotation} scenarios.
Expand All @@ -37,7 +44,7 @@ public class NegativeConstraintTests {
private static final Path RES_DIR = Paths.get("src/test/resources/ballerina-to-openapi").toAbsolutePath();

@Test(description = "When the string constraint has incompatible REGEX patterns with OAS")
public void testInvalidRegexPatterns() throws IOException {
public void testInterpolationInRegexPatterns() throws IOException {
Path ballerinaFilePath = RES_DIR.resolve("constraint-negative/negative_patternInterpolation.bal");
List<OpenAPIConverterDiagnostic> errors = TestUtils.compareWithGeneratedFile(new OASContractGenerator(),
ballerinaFilePath, "constraint-negative/negative_patternInterpolation.yaml");
Expand All @@ -49,4 +56,64 @@ public void testInvalidRegexPatterns() throws IOException {
"REGEX pattern.");
}
}

@Test(description = "When a constraint has values referenced with variables")
public void testConstNameRef() throws IOException {
Path ballerinaFilePath = RES_DIR.resolve("constraint-negative/negative_constNameRef.bal");
List<OpenAPIConverterDiagnostic> errors = TestUtils.compareWithGeneratedFile(new OASContractGenerator(),
ballerinaFilePath, "constraint-negative/negative_constNameRef.yaml");
List<String> expectedVariables = Arrays.asList("maxVal", "5 + minVal", "Value");
for (int i = 0; i < errors.size(); i++) {
Assert.assertEquals(errors.get(i).getMessage(), "Generated OpenAPI definition does not contain" +
" variable assignment '" + expectedVariables.get(i) + "' in constraint validation.");
}
}

/*
* This test is used to check whether it gives warnings when '@constraint:Date' is being used.
* Currently, we don't have support for mapping Date constraints to OAS hence we skip them.
* TODO: <a href="https://github.com/ballerina-platform/ballerina-standard-library/issues/5049">...</a>
* Once the above improvement is completed this Negative test should be removed and should add as a Unit test!
*/
@Test(description = "when the record field has time:Date record type")
public void testDateType() throws IOException {
Path ballerinaFilePath = RES_DIR.resolve("constraint-negative/negative_date.bal");
List<OpenAPIConverterDiagnostic> errors = TestUtils.compareWithGeneratedFile(new OASContractGenerator(),
ballerinaFilePath, "constraint-negative/negative_date.yaml");
errors.forEach(error -> Assert.assertEquals(error.getMessage(), "Ballerina Date constraints might " +
"not be reflected in the OpenAPI definition"));
}

/*
* This test is used to check whether it gives compilation errors when invalid REGEX patterns are given.
* To get more context go through the referenced links.
* By fixing below Bug will make this test fail.
* {@link <a href="https://github.com/ballerina-platform/ballerina-lang/issues/41492">...</a>}
* TODO: <a href="https://github.com/ballerina-platform/ballerina-standard-library/issues/5048">...</a>
*/
@Test(description = "When String constraint has compilation errors (REGEX pattern with invalid format)")
public void testInvalidRegexPattern() throws ProjectException {
Path ballerinaFilePath = RES_DIR.resolve("constraint-negative/invalidRegexPattern.bal");
Project project = ProjectLoader.loadProject(ballerinaFilePath);
DiagnosticResult diagnostic = getCompilation(project);
Object[] errors = diagnostic.diagnostics().stream().filter(d ->
DiagnosticSeverity.ERROR == d.diagnosticInfo().severity()).toArray();
Assert.assertEquals(errors.length, 2);
Assert.assertTrue(errors[0].toString().contains("ERROR [invalidRegexPattern.bal:(21:20,21:20)] " +
"invalid char after backslash"));
Assert.assertTrue(errors[1].toString().contains("ERROR [invalidRegexPattern.bal:(21:22,21:22)] " +
"missing backslash"));
}

@Test(description = "When Integer constraint has float value which is invalid")
public void testInvalidInteger() throws ProjectException {
Path ballerinaFilePath = RES_DIR.resolve("constraint-negative/invalidInteger.bal");
Project project = ProjectLoader.loadProject(ballerinaFilePath);
DiagnosticResult diagnostic = getCompilation(project);
Object[] errors = diagnostic.diagnostics().stream().filter(d ->
DiagnosticSeverity.ERROR == d.diagnosticInfo().severity()).toArray();
Assert.assertEquals(errors.length, 1);
Assert.assertTrue(errors[0].toString().contains("incompatible types: expected '(int|record {| int value; " +
"string message; |})?', found 'float'"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@

import io.ballerina.openapi.cmd.OASContractGenerator;
import io.ballerina.openapi.converter.diagnostic.OpenAPIConverterDiagnostic;
import io.ballerina.projects.DiagnosticResult;
import io.ballerina.projects.Package;
import io.ballerina.projects.Project;
import org.testng.Assert;

import java.io.File;
Expand Down Expand Up @@ -84,6 +87,11 @@ public static List<OpenAPIConverterDiagnostic> compareWithGeneratedFile(OASContr
}
}

public static DiagnosticResult getCompilation(Project project) {
Package cPackage = project.currentPackage();
return cPackage.getCompilation().diagnosticResult();
}

public static void deleteDirectory(Path path) {
if (!Files.exists(path)) {
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org).
//
// WSO2 LLC. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

import ballerina/http;
import ballerina/constraint;

@constraint:Int{
minValueExclusive: 10.5
}
public type Distance int;

public type City record{
Distance distance;
};

service /payloadV on new http:Listener(9090) {
resource function post pet(@http:Payload City body) returns error? {
return;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org).
//
// WSO2 LLC. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

import ballerina/http;
import ballerina/constraint;

@constraint:String {
pattern: re `^\${1,2}[a-z]+$`
}
public type Position string;

public type Child record {
Position position;
};

service /payloadV on new http:Listener(9090) {
resource function post pet(@http:Payload Child body) returns error? {
return;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org).
//
// WSO2 LLC. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

import ballerina/http;
import ballerina/constraint;

int maxVal = 100;
int minVal = 5;
const float Value = 10.5;
@constraint:Int {
maxValueExclusive: maxVal,
minValue: {
value: 5 + minVal,
message: "Min value exceeded!"
}
}
public type St_ID int;

public type StudentRecord record {
St_ID id;
@constraint:Float { minValue: Value}
float num?;
};

service /payloadV on new http:Listener(9090) {
resource function post pet(@http:Payload StudentRecord body) returns error? {
return;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org).
//
// WSO2 LLC. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

import ballerina/http;
import ballerina/constraint;
import ballerina/time;

@constraint:Date {
option: constraint:FUTURE,
message: "Event date cannot be past!"
}
public type MyDate time:Date;

type RegDate record {
MyDate date;
@constraint:Date {
option: {
value: constraint:PAST,
message: "Last login should be past!"
},
message: "Invalid date found for last login!"
}
time:Date lastLogin?;
};

service /payloadV on new http:Listener(9090) {
resource function post pet(RegDate body) returns error? {
return;
}
}
Loading

0 comments on commit adbdc1e

Please sign in to comment.