Repository: lukeautry/tsoa (ts)

Issue:
jsDoc cannot contain '@'
<!--- Provide a general summary of the issue in the Title above -->

## Sorting

* **I'm submitting a ...**
  - [X] bug report
  - [ ] feature request
  - [ ] support request

* I confirm that I
  - [X] used the [search](https://github.com/lukeautry/tsoa/search?type=Issues) to make sure that a similar issue hasn't already been submit

## Expected Behavior

```ts
/**
 * @format email 
 * @pattern ^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$ 
 */
email: string
``` 
It should create a validator containing the full pattern :

```ts
"email": { 
  "dataType": "string", 
  "required": true, 
  "validators": { 
    "pattern": { 
      "value": "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$"
    } 
  }
},
```

## Current Behavior

The pattern is split at @ and only the first part of the regex is used

```ts
"email": { 
  "dataType": "string", 
  "required": true, 
  "validators": { 
    "pattern": { 
      "value": "^[a-zA-Z0-9_.+-]+" 
    } 
  } 
},
```

## Context (Environment)
Version of the library: 2.5.12
Version of NodeJS: v10.15.3
* Confirm you were using yarn not npm: [X]


Pull request description:
allow quoting of patterns with "`" #563
### All Submissions:

* [x] Have you followed the guidelines in our [Contributing](https://github.com/lukeautry/tsoa/tree/master/docs/CONTRIBUTING.md) document?
* [x] Have you checked to ensure there aren't other open [Pull Requests](https://github.com/lukeautry/tsoa/pulls) for the same update/change?
* [x] Have you written unit tests?
* [x] Have you written unit tests that cover the negative cases (i.e.: if bad data is submitted, does the library respond properly)?
* [x] This PR is associated with an existing issue?

**Closing issues**

closes #563

**Test plan**
* Added test properties that specifically test regex that contain @ symbols.
* Bugfix solves issue with email property test where only half the regex was included.

Patch:
diff --git a/src/utils/validatorUtils.ts b/src/utils/validatorUtils.ts
index 69f26fa3..f0305c7d 100644
--- a/src/utils/validatorUtils.ts
+++ b/src/utils/validatorUtils.ts
@@ -83,11 +83,11 @@ export function getParameterValidators(parameter: ts.ParameterDeclaration, param
           break;
         case 'pattern':
           if (typeof value !== 'string') {
-            throw new GenerateMetadataError(`${name} patameter use string.`);
+            throw new GenerateMetadataError(`${name} parameter use string.`);
           }
           validateObj[name] = {
             errorMsg: getErrorMsg(comment),
-            value,
+            value: removeSurroundingQuotes(value),
           };
           break;
         default:
@@ -173,11 +173,11 @@ export function getPropertyValidators(property: ts.PropertyDeclaration | ts.Type
           break;
         case 'pattern':
           if (typeof value !== 'string') {
-            throw new GenerateMetadataError(`${name} patameter use string.`);
+            throw new GenerateMetadataError(`${name} parameter use string.`);
           }
           validateObj[name] = {
             errorMsg: getErrorMsg(comment),
-            value,
+            value: removeSurroundingQuotes(value),
           };
           break;
         default:
@@ -220,3 +220,13 @@ function getParameterTagSupport() {
     'maxDate',
   ];
 }
+
+function removeSurroundingQuotes(str: string) {
+  if (str.startsWith('`') && str.endsWith('`')) {
+    return str.substring(1, str.length - 1);
+  }
+  if (str.startsWith('```') && str.endsWith('```')) {
+    return str.substring(3, str.length - 3);
+  }
+  return str;
+}


Test patch:
diff --git a/tests/fixtures/controllers/validateController.ts b/tests/fixtures/controllers/validateController.ts
index 304b61c8..7c111fa0 100644
--- a/tests/fixtures/controllers/validateController.ts
+++ b/tests/fixtures/controllers/validateController.ts
@@ -16,6 +16,7 @@ export interface ValidateStringResponse {
   minLength: string;
   maxLength: string;
   patternValue: string;
+  quotedPatternValue: string;
 }
 
 @Route('Validate')
@@ -99,13 +100,15 @@ export class ValidateController {
    * @minLength minLength 5
    * @maxLength maxLength 3
    * @pattern patternValue ^[a-zA-Z]+$
+   * @pattern quotedPatternValue `^([A-Z])(?!@)$`
    */
   @Get('parameter/string')
-  public stringValidate(@Query() minLength: string, @Query() maxLength: string, @Query() patternValue: string): Promise<ValidateStringResponse> {
+  public stringValidate(@Query() minLength: string, @Query() maxLength: string, @Query() patternValue: string, @Query() quotedPatternValue: string): Promise<ValidateStringResponse> {
     return Promise.resolve({
       maxLength,
       minLength,
       patternValue,
+      quotedPatternValue,
     });
   }
   /**
diff --git a/tests/fixtures/testModel.ts b/tests/fixtures/testModel.ts
index 75e27d8e..69f06f45 100644
--- a/tests/fixtures/testModel.ts
+++ b/tests/fixtures/testModel.ts
@@ -361,7 +361,10 @@ export class ValidateModel {
    *  @pattern ^[a-zA-Z]+$
    */
   public stringPatternAZaz: string;
-
+  /**
+   * @pattern `^([A-Z])(?!@)$`
+   */
+  public quotedStringPatternA: string;
   /**
    * @maxItems 5
    */
@@ -463,7 +466,10 @@ export class ValidateModel {
      *  @pattern ^[a-zA-Z]+$
      */
     stringPatternAZaz: string;
-
+    /**
+     * @pattern `^([A-Z])(?!@)$`
+     */
+    quotedStringPatternA: string;
     /**
      * @maxItems 5
      */
@@ -569,7 +575,7 @@ export class TestClassModel extends TestClassBaseModel {
   public optionalPublicStringProperty?: string;
   /**
    * @format email
-   * @pattern ^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$
+   * @pattern `^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$`
    */
   public emailPattern?: string;
   /* tslint:disable-next-line */
diff --git a/tests/integration/dynamic-controllers-express-server.spec.ts b/tests/integration/dynamic-controllers-express-server.spec.ts
index 060cea40..1e64e504 100644
--- a/tests/integration/dynamic-controllers-express-server.spec.ts
+++ b/tests/integration/dynamic-controllers-express-server.spec.ts
@@ -445,24 +445,25 @@ describe('Express Server', () => {
       );
     });
 
-    it('should valid minLength, maxLength and pattern validation of string type', () => {
+    it('should valid minLength, maxLength and pattern (quoted/unquoted) validation of string type', () => {
       return verifyGetRequest(
-        basePath + `/Validate/parameter/string?minLength=abcdef&maxLength=ab&patternValue=aBcDf`,
+        basePath + `/Validate/parameter/string?minLength=abcdef&maxLength=ab&patternValue=aBcDf&quotedPatternValue=A`,
         (err, res) => {
           const { body } = res;
 
           expect(body.minLength).to.equal('abcdef');
           expect(body.maxLength).to.equal('ab');
           expect(body.patternValue).to.equal('aBcDf');
+          expect(body.quotedPatternValue).to.equal('A');
         },
         200,
       );
     });
 
-    it('should invalid minLength, maxLength and pattern validation of string type', () => {
+    it('should invalid minLength, maxLength and pattern (quoted/unquoted) validation of string type', () => {
       const value = '1234';
       return verifyGetRequest(
-        basePath + `/Validate/parameter/string?minLength=${value}&maxLength=${value}&patternValue=${value}`,
+        basePath + `/Validate/parameter/string?minLength=${value}&maxLength=${value}&patternValue=${value}&quotedPatternValue=A@`,
         (err, res) => {
           const body = JSON.parse(err.text);
 
@@ -472,6 +473,8 @@ describe('Express Server', () => {
           expect(body.fields.maxLength.value).to.equal(value);
           expect(body.fields.patternValue.message).to.equal("Not match in '^[a-zA-Z]+$'");
           expect(body.fields.patternValue.value).to.equal(value);
+          expect(body.fields.quotedPatternValue.message).to.equal("Not match in '^([A-Z])(?!@)$'");
+          expect(body.fields.quotedPatternValue.value).to.equal('A@');
         },
         400,
       );
@@ -493,6 +496,7 @@ describe('Express Server', () => {
       bodyModel.stringMax10Lenght = 'abcdef';
       bodyModel.stringMin5Lenght = 'abcdef';
       bodyModel.stringPatternAZaz = 'aBcD';
+      bodyModel.quotedStringPatternA = 'A';
 
       bodyModel.arrayMax5Item = [0, 1, 2, 3];
       bodyModel.arrayMin2Item = [0, 1];
@@ -517,6 +521,7 @@ describe('Express Server', () => {
         stringMax10Lenght: 'abcdef',
         stringMin5Lenght: 'abcdef',
         stringPatternAZaz: 'aBcD',
+        quotedStringPatternA: 'A',
 
         arrayMax5Item: [0, 1, 2, 3],
         arrayMin2Item: [0, 1],
@@ -567,6 +572,7 @@ describe('Express Server', () => {
           expect(body.stringMax10Lenght).to.equal(bodyModel.stringMax10Lenght);
           expect(body.stringMin5Lenght).to.equal(bodyModel.stringMin5Lenght);
           expect(body.stringPatternAZaz).to.equal(bodyModel.stringPatternAZaz);
+          expect(body.quotedStringPatternA).to.equal(bodyModel.quotedStringPatternA);
 
           expect(body.arrayMax5Item).to.deep.equal(bodyModel.arrayMax5Item);
           expect(body.arrayMin2Item).to.deep.equal(bodyModel.arrayMin2Item);
@@ -591,6 +597,7 @@ describe('Express Server', () => {
           expect(body.nestedObject.stringMax10Lenght).to.equal(bodyModel.nestedObject.stringMax10Lenght);
           expect(body.nestedObject.stringMin5Lenght).to.equal(bodyModel.nestedObject.stringMin5Lenght);
           expect(body.nestedObject.stringPatternAZaz).to.equal(bodyModel.nestedObject.stringPatternAZaz);
+          expect(body.nestedObject.quotedStringPatternA).to.equal(bodyModel.nestedObject.quotedStringPatternA);
 
           expect(body.nestedObject.arrayMax5Item).to.deep.equal(bodyModel.nestedObject.arrayMax5Item);
           expect(body.nestedObject.arrayMin2Item).to.deep.equal(bodyModel.nestedObject.arrayMin2Item);
@@ -624,6 +631,7 @@ describe('Express Server', () => {
       bodyModel.stringMax10Lenght = 'abcdefghijk';
       bodyModel.stringMin5Lenght = 'abcd';
       bodyModel.stringPatternAZaz = 'ab01234';
+      bodyModel.quotedStringPatternA = 'A';
 
       bodyModel.arrayMax5Item = [0, 1, 2, 3, 4, 6, 7, 8, 9];
       bodyModel.arrayMin2Item = [0];
@@ -647,6 +655,7 @@ describe('Express Server', () => {
         stringMax10Lenght: 'abcdefghijk',
         stringMin5Lenght: 'abcd',
         stringPatternAZaz: 'ab01234',
+        quotedStringPatternA: 'A@',
 
         arrayMax5Item: [0, 1, 2, 3, 4, 6, 7, 8, 9],
         arrayMin2Item: [0],
@@ -724,6 +733,7 @@ describe('Express Server', () => {
           );
           expect(body.fields['body.intersection'].message).to.equal('Could not match the intersection against every type. Issues: [{"body.intersection.value2":{"message":"\'value2\' is required"}}]');
           expect(body.fields['body.singleBooleanEnum'].message).to.equal('should be one of the following; [true]');
+
           expect(body.fields['body.nestedObject.floatValue'].message).to.equal('Invalid float error message.');
           expect(body.fields['body.nestedObject.floatValue'].value).to.equal(bodyModel.floatValue);
           expect(body.fields['body.nestedObject.doubleValue'].message).to.equal('Invalid double error message.');
diff --git a/tests/integration/express-server.spec.ts b/tests/integration/express-server.spec.ts
index 67db413c..10f99d80 100644
--- a/tests/integration/express-server.spec.ts
+++ b/tests/integration/express-server.spec.ts
@@ -483,24 +483,25 @@ describe('Express Server', () => {
       );
     });
 
-    it('should valid minLength, maxLength and pattern validation of string type', () => {
+    it('should valid minLength, maxLength and pattern (quoted/unquoted) validation of string type', () => {
       return verifyGetRequest(
-        basePath + `/Validate/parameter/string?minLength=abcdef&maxLength=ab&patternValue=aBcDf`,
+        basePath + `/Validate/parameter/string?minLength=abcdef&maxLength=ab&patternValue=aBcDf&quotedPatternValue=A`,
         (err, res) => {
           const { body } = res;
 
           expect(body.minLength).to.equal('abcdef');
           expect(body.maxLength).to.equal('ab');
           expect(body.patternValue).to.equal('aBcDf');
+          expect(body.quotedPatternValue).to.equal('A');
         },
         200,
       );
     });
 
-    it('should invalid minLength, maxLength and pattern validation of string type', () => {
+    it('should invalid minLength, maxLength and pattern (quoted/unquoted) validation of string type', () => {
       const value = '1234';
       return verifyGetRequest(
-        basePath + `/Validate/parameter/string?minLength=${value}&maxLength=${value}&patternValue=${value}`,
+        basePath + `/Validate/parameter/string?minLength=${value}&maxLength=${value}&patternValue=${value}&quotedPatternValue=A@`,
         (err, res) => {
           const body = JSON.parse(err.text);
 
@@ -510,6 +511,8 @@ describe('Express Server', () => {
           expect(body.fields.maxLength.value).to.equal(value);
           expect(body.fields.patternValue.message).to.equal("Not match in '^[a-zA-Z]+$'");
           expect(body.fields.patternValue.value).to.equal(value);
+          expect(body.fields.quotedPatternValue.message).to.equal("Not match in '^([A-Z])(?!@)$'");
+          expect(body.fields.quotedPatternValue.value).to.equal('A@');
         },
         400,
       );
@@ -531,6 +534,7 @@ describe('Express Server', () => {
       bodyModel.stringMax10Lenght = 'abcdef';
       bodyModel.stringMin5Lenght = 'abcdef';
       bodyModel.stringPatternAZaz = 'aBcD';
+      bodyModel.quotedStringPatternA = 'A';
 
       bodyModel.arrayMax5Item = [0, 1, 2, 3];
       bodyModel.arrayMin2Item = [0, 1];
@@ -555,6 +559,7 @@ describe('Express Server', () => {
         stringMax10Lenght: 'abcdef',
         stringMin5Lenght: 'abcdef',
         stringPatternAZaz: 'aBcD',
+        quotedStringPatternA: 'A',
 
         arrayMax5Item: [0, 1, 2, 3],
         arrayMin2Item: [0, 1],
@@ -605,6 +610,7 @@ describe('Express Server', () => {
           expect(body.stringMax10Lenght).to.equal(bodyModel.stringMax10Lenght);
           expect(body.stringMin5Lenght).to.equal(bodyModel.stringMin5Lenght);
           expect(body.stringPatternAZaz).to.equal(bodyModel.stringPatternAZaz);
+          expect(body.quotedStringPatternA).to.equal(bodyModel.quotedStringPatternA);
 
           expect(body.arrayMax5Item).to.deep.equal(bodyModel.arrayMax5Item);
           expect(body.arrayMin2Item).to.deep.equal(bodyModel.arrayMin2Item);
@@ -629,6 +635,7 @@ describe('Express Server', () => {
           expect(body.nestedObject.stringMax10Lenght).to.equal(bodyModel.nestedObject.stringMax10Lenght);
           expect(body.nestedObject.stringMin5Lenght).to.equal(bodyModel.nestedObject.stringMin5Lenght);
           expect(body.nestedObject.stringPatternAZaz).to.equal(bodyModel.nestedObject.stringPatternAZaz);
+          expect(body.nestedObject.quotedStringPatternA).to.equal(bodyModel.nestedObject.quotedStringPatternA);
 
           expect(body.nestedObject.arrayMax5Item).to.deep.equal(bodyModel.nestedObject.arrayMax5Item);
           expect(body.nestedObject.arrayMin2Item).to.deep.equal(bodyModel.nestedObject.arrayMin2Item);
@@ -662,6 +669,7 @@ describe('Express Server', () => {
       bodyModel.stringMax10Lenght = 'abcdefghijk';
       bodyModel.stringMin5Lenght = 'abcd';
       bodyModel.stringPatternAZaz = 'ab01234';
+      bodyModel.quotedStringPatternA = 'A';
 
       bodyModel.arrayMax5Item = [0, 1, 2, 3, 4, 6, 7, 8, 9];
       bodyModel.arrayMin2Item = [0];
@@ -685,6 +693,7 @@ describe('Express Server', () => {
         stringMax10Lenght: 'abcdefghijk',
         stringMin5Lenght: 'abcd',
         stringPatternAZaz: 'ab01234',
+        quotedStringPatternA: 'A@',
 
         arrayMax5Item: [0, 1, 2, 3, 4, 6, 7, 8, 9],
         arrayMin2Item: [0],
diff --git a/tests/integration/hapi-server.spec.ts b/tests/integration/hapi-server.spec.ts
index 6a0202a5..6c723c16 100644
--- a/tests/integration/hapi-server.spec.ts
+++ b/tests/integration/hapi-server.spec.ts
@@ -418,24 +418,25 @@ describe('Hapi Server', () => {
       );
     });
 
-    it('should valid minLength, maxLength and pattern validation of string type', () => {
+    it('should valid minLength, maxLength and pattern (quoted/unquoted) validation of string type', () => {
       return verifyGetRequest(
-        basePath + `/Validate/parameter/string?minLength=abcdef&maxLength=ab&patternValue=aBcDf`,
+        basePath + `/Validate/parameter/string?minLength=abcdef&maxLength=ab&patternValue=aBcDf&quotedPatternValue=A`,
         (err, res) => {
           const { body } = res;
 
           expect(body.minLength).to.equal('abcdef');
           expect(body.maxLength).to.equal('ab');
           expect(body.patternValue).to.equal('aBcDf');
+          expect(body.quotedPatternValue).to.equal('A');
         },
         200,
       );
     });
 
-    it('should invalid minLength, maxLength and pattern validation of string type', () => {
+    it('should invalid minLength, maxLength and pattern (quoted/unquoted) validation of string type', () => {
       const value = '1234';
       return verifyGetRequest(
-        basePath + `/Validate/parameter/string?minLength=${value}&maxLength=${value}&patternValue=${value}`,
+        basePath + `/Validate/parameter/string?minLength=${value}&maxLength=${value}&patternValue=${value}&quotedPatternValue=A@`,
         (err, res) => {
           const body = JSON.parse(err.text);
 
@@ -445,6 +446,8 @@ describe('Hapi Server', () => {
           expect(body.fields.maxLength.value).to.equal(value);
           expect(body.fields.patternValue.message).to.equal("Not match in '^[a-zA-Z]+$'");
           expect(body.fields.patternValue.value).to.equal(value);
+          expect(body.fields.quotedPatternValue.message).to.equal("Not match in '^([A-Z])(?!@)$'");
+          expect(body.fields.quotedPatternValue.value).to.equal('A@');
         },
         400,
       );
@@ -466,6 +469,7 @@ describe('Hapi Server', () => {
       bodyModel.stringMax10Lenght = 'abcdef';
       bodyModel.stringMin5Lenght = 'abcdef';
       bodyModel.stringPatternAZaz = 'aBcD';
+      bodyModel.quotedStringPatternA = 'A';
 
       bodyModel.arrayMax5Item = [0, 1, 2, 3];
       bodyModel.arrayMin2Item = [0, 1];
@@ -490,6 +494,7 @@ describe('Hapi Server', () => {
         stringMax10Lenght: 'abcdef',
         stringMin5Lenght: 'abcdef',
         stringPatternAZaz: 'aBcD',
+        quotedStringPatternA: 'A',
 
         arrayMax5Item: [0, 1, 2, 3],
         arrayMin2Item: [0, 1],
@@ -540,6 +545,7 @@ describe('Hapi Server', () => {
           expect(body.stringMax10Lenght).to.equal(bodyModel.stringMax10Lenght);
           expect(body.stringMin5Lenght).to.equal(bodyModel.stringMin5Lenght);
           expect(body.stringPatternAZaz).to.equal(bodyModel.stringPatternAZaz);
+          expect(body.quotedStringPatternA).to.equal(bodyModel.quotedStringPatternA);
 
           expect(body.arrayMax5Item).to.deep.equal(bodyModel.arrayMax5Item);
           expect(body.arrayMin2Item).to.deep.equal(bodyModel.arrayMin2Item);
@@ -564,6 +570,7 @@ describe('Hapi Server', () => {
           expect(body.nestedObject.stringMax10Lenght).to.equal(bodyModel.nestedObject.stringMax10Lenght);
           expect(body.nestedObject.stringMin5Lenght).to.equal(bodyModel.nestedObject.stringMin5Lenght);
           expect(body.nestedObject.stringPatternAZaz).to.equal(bodyModel.nestedObject.stringPatternAZaz);
+          expect(body.nestedObject.quotedStringPatternA).to.equal(bodyModel.nestedObject.quotedStringPatternA);
 
           expect(body.nestedObject.arrayMax5Item).to.deep.equal(bodyModel.nestedObject.arrayMax5Item);
           expect(body.nestedObject.arrayMin2Item).to.deep.equal(bodyModel.nestedObject.arrayMin2Item);
@@ -597,6 +604,7 @@ describe('Hapi Server', () => {
       bodyModel.stringMax10Lenght = 'abcdefghijk';
       bodyModel.stringMin5Lenght = 'abcd';
       bodyModel.stringPatternAZaz = 'ab01234';
+      bodyModel.quotedStringPatternA = 'A';
 
       bodyModel.arrayMax5Item = [0, 1, 2, 3, 4, 6, 7, 8, 9];
       bodyModel.arrayMin2Item = [0];
@@ -620,6 +628,7 @@ describe('Hapi Server', () => {
         stringMax10Lenght: 'abcdefghijk',
         stringMin5Lenght: 'abcd',
         stringPatternAZaz: 'ab01234',
+        quotedStringPatternA: 'A@',
 
         arrayMax5Item: [0, 1, 2, 3, 4, 6, 7, 8, 9],
         arrayMin2Item: [0],
diff --git a/tests/integration/koa-server-no-additional-allowed.spec.ts b/tests/integration/koa-server-no-additional-allowed.spec.ts
index ef249b88..481bbac4 100644
--- a/tests/integration/koa-server-no-additional-allowed.spec.ts
+++ b/tests/integration/koa-server-no-additional-allowed.spec.ts
@@ -188,6 +188,7 @@ describe('Koa Server (with noImplicitAdditionalProperties turned on)', () => {
       bodyModel.stringMax10Lenght = 'abcdef';
       bodyModel.stringMin5Lenght = 'abcdef';
       bodyModel.stringPatternAZaz = 'aBcD';
+      bodyModel.quotedStringPatternA = 'A';
 
       bodyModel.arrayMax5Item = [0, 1, 2, 3];
       bodyModel.arrayMin2Item = [0, 1];
@@ -212,6 +213,7 @@ describe('Koa Server (with noImplicitAdditionalProperties turned on)', () => {
         stringMax10Lenght: 'abcdef',
         stringMin5Lenght: 'abcdef',
         stringPatternAZaz: 'aBcD',
+        quotedStringPatternA: 'A',
 
         arrayMax5Item: [0, 1, 2, 3],
         arrayMin2Item: [0, 1],
@@ -262,6 +264,7 @@ describe('Koa Server (with noImplicitAdditionalProperties turned on)', () => {
           expect(body.stringMax10Lenght).to.equal(bodyModel.stringMax10Lenght);
           expect(body.stringMin5Lenght).to.equal(bodyModel.stringMin5Lenght);
           expect(body.stringPatternAZaz).to.equal(bodyModel.stringPatternAZaz);
+          expect(body.quotedStringPatternA).to.equal(bodyModel.quotedStringPatternA);
 
           expect(body.arrayMax5Item).to.deep.equal(bodyModel.arrayMax5Item);
           expect(body.arrayMin2Item).to.deep.equal(bodyModel.arrayMin2Item);
@@ -286,6 +289,7 @@ describe('Koa Server (with noImplicitAdditionalProperties turned on)', () => {
           expect(body.nestedObject.stringMax10Lenght).to.equal(bodyModel.nestedObject.stringMax10Lenght);
           expect(body.nestedObject.stringMin5Lenght).to.equal(bodyModel.nestedObject.stringMin5Lenght);
           expect(body.nestedObject.stringPatternAZaz).to.equal(bodyModel.nestedObject.stringPatternAZaz);
+          expect(body.nestedObject.quotedStringPatternA).to.equal(bodyModel.nestedObject.quotedStringPatternA);
 
           expect(body.nestedObject.arrayMax5Item).to.deep.equal(bodyModel.nestedObject.arrayMax5Item);
           expect(body.nestedObject.arrayMin2Item).to.deep.equal(bodyModel.nestedObject.arrayMin2Item);
diff --git a/tests/integration/koa-server.spec.ts b/tests/integration/koa-server.spec.ts
index 42a19534..6fd616d2 100644
--- a/tests/integration/koa-server.spec.ts
+++ b/tests/integration/koa-server.spec.ts
@@ -396,24 +396,25 @@ describe('Koa Server', () => {
       );
     });
 
-    it('should valid minLength, maxLength and pattern validation of string type', () => {
+    it('should valid minLength, maxLength and pattern (quoted/unquoted) validation of string type', () => {
       return verifyGetRequest(
-        basePath + `/Validate/parameter/string?minLength=abcdef&maxLength=ab&patternValue=aBcDf`,
+        basePath + `/Validate/parameter/string?minLength=abcdef&maxLength=ab&patternValue=aBcDf&quotedPatternValue=A`,
         (err, res) => {
           const { body } = res;
 
           expect(body.minLength).to.equal('abcdef');
           expect(body.maxLength).to.equal('ab');
           expect(body.patternValue).to.equal('aBcDf');
+          expect(body.quotedPatternValue).to.equal('A');
         },
         200,
       );
     });
 
-    it('should invalid minLength, maxLength and pattern validation of string type', () => {
+    it('should invalid minLength, maxLength and pattern (quoted/unquoted) validation of string type', () => {
       const value = '1234';
       return verifyGetRequest(
-        basePath + `/Validate/parameter/string?minLength=${value}&maxLength=${value}&patternValue=${value}`,
+        basePath + `/Validate/parameter/string?minLength=${value}&maxLength=${value}&patternValue=${value}&quotedPatternValue=A@`,
         (err, res) => {
           const body = JSON.parse(err.text);
 
@@ -423,6 +424,8 @@ describe('Koa Server', () => {
           expect(body.fields.maxLength.value).to.equal(value);
           expect(body.fields.patternValue.message).to.equal("Not match in '^[a-zA-Z]+$'");
           expect(body.fields.patternValue.value).to.equal(value);
+          expect(body.fields.quotedPatternValue.message).to.equal("Not match in '^([A-Z])(?!@)$'");
+          expect(body.fields.quotedPatternValue.value).to.equal('A@');
         },
         400,
       );
@@ -444,6 +447,7 @@ describe('Koa Server', () => {
       bodyModel.stringMax10Lenght = 'abcdef';
       bodyModel.stringMin5Lenght = 'abcdef';
       bodyModel.stringPatternAZaz = 'aBcD';
+      bodyModel.quotedStringPatternA = 'A';
 
       bodyModel.arrayMax5Item = [0, 1, 2, 3];
       bodyModel.arrayMin2Item = [0, 1];
@@ -468,6 +472,7 @@ describe('Koa Server', () => {
         stringMax10Lenght: 'abcdef',
         stringMin5Lenght: 'abcdef',
         stringPatternAZaz: 'aBcD',
+        quotedStringPatternA: 'A',
 
         arrayMax5Item: [0, 1, 2, 3],
         arrayMin2Item: [0, 1],
@@ -518,6 +523,7 @@ describe('Koa Server', () => {
           expect(body.stringMax10Lenght).to.equal(bodyModel.stringMax10Lenght);
           expect(body.stringMin5Lenght).to.equal(bodyModel.stringMin5Lenght);
           expect(body.stringPatternAZaz).to.equal(bodyModel.stringPatternAZaz);
+          expect(body.quotedStringPatternA).to.equal(bodyModel.quotedStringPatternA);
 
           expect(body.arrayMax5Item).to.deep.equal(bodyModel.arrayMax5Item);
           expect(body.arrayMin2Item).to.deep.equal(bodyModel.arrayMin2Item);
@@ -542,6 +548,7 @@ describe('Koa Server', () => {
           expect(body.nestedObject.stringMax10Lenght).to.equal(bodyModel.nestedObject.stringMax10Lenght);
           expect(body.nestedObject.stringMin5Lenght).to.equal(bodyModel.nestedObject.stringMin5Lenght);
           expect(body.nestedObject.stringPatternAZaz).to.equal(bodyModel.nestedObject.stringPatternAZaz);
+          expect(body.nestedObject.quotedStringPatternA).to.equal(bodyModel.nestedObject.quotedStringPatternA);
 
           expect(body.nestedObject.arrayMax5Item).to.deep.equal(bodyModel.nestedObject.arrayMax5Item);
           expect(body.nestedObject.arrayMin2Item).to.deep.equal(bodyModel.nestedObject.arrayMin2Item);
@@ -575,6 +582,7 @@ describe('Koa Server', () => {
       bodyModel.stringMax10Lenght = 'abcdefghijk';
       bodyModel.stringMin5Lenght = 'abcd';
       bodyModel.stringPatternAZaz = 'ab01234';
+      bodyModel.quotedStringPatternA = 'A';
 
       bodyModel.arrayMax5Item = [0, 1, 2, 3, 4, 6, 7, 8, 9];
       bodyModel.arrayMin2Item = [0];
@@ -598,6 +606,7 @@ describe('Koa Server', () => {
         stringMax10Lenght: 'abcdefghijk',
         stringMin5Lenght: 'abcd',
         stringPatternAZaz: 'ab01234',
+        quotedStringPatternA: 'A@',
 
         arrayMax5Item: [0, 1, 2, 3, 4, 6, 7, 8, 9],
         arrayMin2Item: [0],
diff --git a/tests/integration/openapi3-express.spec.ts b/tests/integration/openapi3-express.spec.ts
index 79142c2d..1527a8a1 100644
--- a/tests/integration/openapi3-express.spec.ts
+++ b/tests/integration/openapi3-express.spec.ts
@@ -23,6 +23,7 @@ describe('OpenAPI3 Express Server', () => {
     bodyModel.stringMax10Lenght = 'abcdef';
     bodyModel.stringMin5Lenght = 'abcdef';
     bodyModel.stringPatternAZaz = 'aBcD';
+    bodyModel.quotedStringPatternA = 'A';
 
     bodyModel.arrayMax5Item = [0, 1, 2, 3];
     bodyModel.arrayMin2Item = [0, 1];
@@ -47,6 +48,7 @@ describe('OpenAPI3 Express Server', () => {
       stringMax10Lenght: 'abcdef',
       stringMin5Lenght: 'abcdef',
       stringPatternAZaz: 'aBcD',
+      quotedStringPatternA: 'A',
 
       arrayMax5Item: [0, 1, 2, 3],
       arrayMin2Item: [0, 1],
@@ -102,6 +104,7 @@ describe('OpenAPI3 Express Server', () => {
         expect(body.stringMax10Lenght).to.equal(bodyModel.stringMax10Lenght);
         expect(body.stringMin5Lenght).to.equal(bodyModel.stringMin5Lenght);
         expect(body.stringPatternAZaz).to.equal(bodyModel.stringPatternAZaz);
+        expect(body.quotedStringPatternA).to.equal(bodyModel.quotedStringPatternA);
 
         expect(body.arrayMax5Item).to.deep.equal(bodyModel.arrayMax5Item);
         expect(body.arrayMin2Item).to.deep.equal(bodyModel.arrayMin2Item);
@@ -126,6 +129,7 @@ describe('OpenAPI3 Express Server', () => {
         expect(body.nestedObject.stringMax10Lenght).to.equal(bodyModel.nestedObject.stringMax10Lenght);
         expect(body.nestedObject.stringMin5Lenght).to.equal(bodyModel.nestedObject.stringMin5Lenght);
         expect(body.nestedObject.stringPatternAZaz).to.equal(bodyModel.nestedObject.stringPatternAZaz);
+        expect(body.nestedObject.quotedStringPatternA).to.equal(bodyModel.nestedObject.quotedStringPatternA);
 
         expect(body.nestedObject.arrayMax5Item).to.deep.equal(bodyModel.nestedObject.arrayMax5Item);
         expect(body.nestedObject.arrayMin2Item).to.deep.equal(bodyModel.nestedObject.arrayMin2Item);
@@ -161,6 +165,7 @@ describe('OpenAPI3 Express Server', () => {
     bodyModel.stringMax10Lenght = 'abcdefghijk';
     bodyModel.stringMin5Lenght = 'abcd';
     bodyModel.stringPatternAZaz = 'ab01234';
+    bodyModel.quotedStringPatternA = 'A';
 
     bodyModel.arrayMax5Item = [0, 1, 2, 3, 4, 6, 7, 8, 9];
     bodyModel.arrayMin2Item = [0];
@@ -169,6 +174,7 @@ describe('OpenAPI3 Express Server', () => {
     bodyModel.intersectionNoAdditional = { value1: '', value2: '', value3: 123, value4: 123 } as any;
     bodyModel.model = 1 as any;
     bodyModel.mixedUnion = 123 as any;
+    bodyModel.intersection = { value1: 'one' } as any;
     bodyModel.singleBooleanEnum = false as true;
 
     bodyModel.nestedObject = {
@@ -185,6 +191,7 @@ describe('OpenAPI3 Express Server', () => {
       stringMax10Lenght: 'abcdefghijk',
       stringMin5Lenght: 'abcd',
       stringPatternAZaz: 'ab01234',
+      quotedStringPatternA: 'A@',
 
       arrayMax5Item: [0, 1, 2, 3, 4, 6, 7, 8, 9],
       arrayMin2Item: [0],
diff --git a/tests/unit/swagger/definitionsGeneration/definitions.spec.ts b/tests/unit/swagger/definitionsGeneration/definitions.spec.ts
index 6f3af595..254e6089 100644
--- a/tests/unit/swagger/definitionsGeneration/definitions.spec.ts
+++ b/tests/unit/swagger/definitionsGeneration/definitions.spec.ts
@@ -807,7 +807,7 @@ describe('Definition generation', () => {
                   readonlyConstructorArgument: { type: 'string', default: undefined, description: undefined, format: undefined },
                   publicConstructorVar: { type: 'string', default: undefined, description: 'This is a description for publicConstructorVar', format: undefined },
                   stringProperty: { type: 'string', default: undefined, description: undefined, format: undefined },
-                  emailPattern: { type: 'string', default: undefined, description: undefined, format: 'email', pattern: '^[a-zA-Z0-9_.+-]+' },
+                  emailPattern: { type: 'string', default: undefined, description: undefined, format: 'email', pattern: '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$' },
                   optionalPublicStringProperty: { type: 'string', minLength: 0, maxLength: 10, default: undefined, description: undefined, format: undefined },
                   publicStringProperty: {
                     type: 'string',
diff --git a/tests/unit/swagger/schemaDetails3.spec.ts b/tests/unit/swagger/schemaDetails3.spec.ts
index ec71b34d..275c7c8c 100644
--- a/tests/unit/swagger/schemaDetails3.spec.ts
+++ b/tests/unit/swagger/schemaDetails3.spec.ts
@@ -970,7 +970,7 @@ describe('Definition generation for OpenAPI 3.0.0', () => {
                   readonlyConstructorArgument: { type: 'string', default: undefined, description: undefined, format: undefined },
                   publicConstructorVar: { type: 'string', default: undefined, description: 'This is a description for publicConstructorVar', format: undefined },
                   stringProperty: { type: 'string', default: undefined, description: undefined, format: undefined },
-                  emailPattern: { type: 'string', default: undefined, description: undefined, format: 'email', pattern: '^[a-zA-Z0-9_.+-]+' },
+                  emailPattern: { type: 'string', default: undefined, description: undefined, format: 'email', pattern: '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$' },
                   optionalPublicStringProperty: { type: 'string', minLength: 0, maxLength: 10, default: undefined, description: undefined, format: undefined },
                   publicStringProperty: {
                     type: 'string',